Kotlin入门学习- - 1

背景介绍

Kotlin介绍

Kotlin 是一个用于现代对平台应用的静态类型编程语言,被称作是Android界的Swift,由JetBrains设计开发的开源语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。2017年Google I/O大会,被宣布为Android的官方开发语言。

为什么要选择使用Kotlin

  • 简洁: 和用Java进行Android开发来说,它大大减少代码量;
  • 安全: 避免空指针异常等整个类的错误;
  • 互操作性:充分利用JVM 、 Android和浏览器中现有的库;
  • 工具友好 可用任何Java IDE或者使用命令行构建;

Kotlin入门

1、变量声明

Kotlin中声明变量的方式就两个关键字:

  1. var 关键字,用于声明变量,这个乍一看的话跟JavaScript中的声明方式是一样的,如果这么认为那就错了,Kotlin不像JavaScript是一种弱类型的语言,除了要使用var还要指定数据的类型比如说(Int 、 Byte、Short、Long、Float、Double 、String等等,之后会介绍Kotlin的数据类型)。比如:
    var countNum: Int = 10  //声明一个Int类型的变量countNum 并赋值为10
    countNum = 100 //重新给countNum赋值
    
  2. val 关键字,用于声明常量,及不可变量,无法给使用该关键字声明的变量进行重新赋值;
    val languageName: String = "Kotlin"
    

2、类型推断

从上面的例子中,当为languageName赋予初始值时,Kotlin编译器可根据所赋值的类型来推断类型。
由于“Kotlin”的值类型为String,因此编译器推断languageName也是String。Kotlin是一种静态类型的语言,这意味着类型在编译时解析且不会改变。Kotlin的类型推断,既能确保代码简洁,又能确保类型安全。比如下面的代码:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()
languageName.inc()//该行编译会失败,因为Kotlin推断languageName的类型为String ,不能调Int的运算符函数

3、NULL安全

在Java中,可以声明引用型变量而不明确提供初始值。在这些情况下,变量通常包含null值。默认情况下,Kotlin变量不能持有null值,这意味着如下代码是无效的:

val languageName: String = null

如果你想将一个变量赋值为null,它必须是可以为null类型。在变量类型后面加上?后缀,将变量指定为可为null,如下:

val languageName: String? = null

指定String? 类型后,您可以为languageName赋予String 值或null。但是要小心处理可为null的变量,否则可能会像Java一样出现NullPointerException,对null值调用方法,您的程序会发生崩溃。

Kotlin提供了多种机制来安全地吹可为null的变量:

互操作性

Kotlin 的严格规则可使代码更安全且更简洁。这些规则可降低会导致应用崩溃的 NullPointerException 出现的几率。此外,它们还可减少您需要在代码中进行的 null 检查的次数。

通常,在编写 Android 应用时,您还必须调用非 Kotlin 代码,因为大多数 Android API 都是用 Java 编程语言编写的。

可为 null 性是 Java 和 Kotlin 在行为上有所不同的一个主要方面。Java 对可为 null 性语法的要求不那么严格。

举例来说,Account 类具有一些属性,包括一个名为 name 的 String 属性。Kotlin 制定了与可为 null 性有关的规则,Java 没有制定这样的规则,而是依赖于可选的可为 null 性注解来明确声明您是否可以赋予 null 值。

由于 Android 框架主要是用 Java 编写的,因此在调用没有可为 null 性注解的 API 时,您可能会遇到这种情况。

平台类型

如果您使用 Kotlin 引用在 Java Account 类中定义的不带注解的 name 成员,编译器将不知道 String 映射到 Kotlin 中的 String 还是 String?。这种不明确通过平台类型 String! 来表示。

String! 对 Kotlin 编译器来说没有特殊的含义。String! 可以表示 String 或 String?,编译器可让您赋予任一类型的值。请注意,如果您将类型表示为 String 并赋予 null 值,则系统可能会抛出 NullPointerException。

要解决此问题,每当您用 Java 编写代码时,都应使用可为 null 性注解。这些注解对 Java 和 Kotlin 开发者都有帮助。

例如,下面是在 Java 中定义的 Account 类:

public class Account implements Parcelable {
        public final String name;
        public final String type;
        private final @Nullable String accessId;

        ...
    }
    

其中一个成员变量 accessId 带有 @Nullable 注解,这表示它可以持有 null 值。于是,Kotlin 会将 accessId 视为 String?。

要指明变量绝不能为 null,请使用 @NonNull 注解:

public class Account implements Parcelable {
        public final @NonNull String name;
        ...
    }
    

在这种情况下,name 在 Kotlin 中被视为不可为 null String。

可为 null 性注解包含在所有新增的 Android API 以及许多现有的 Android API 中。许多 Java 库已添加可为 null 性注解,以便为 Kotlin 和 Java 开发者提供更好的支持。

处理可为null性

如果您不确定 Java 类型,则应将其视为可为 null。举例来说,Account 类的 name 成员不带注解,因此您应假定它是一个可为 null String。

如果您要修剪 name 以使其值不包含前导或尾随空格,则可以使用 Kotlin 的 trim 函数。您可以通过几种不同的方式安全地修剪 String?。其中一种方式是使用非 null 断言运算符 !!,如以下示例所示:

val account = Account("name", "type")
    val accountName = account.name!!.trim()
    

!! 运算符将其左侧的所有内容视为非 null,因此,在本例中,您将 name 视为非 null String。如果它左侧表达式的结果为 null,则您的应用会抛出 NullPointerException。此运算符简单快捷,但应谨慎使用,因为它会将 NullPointerException 的实例重新引入您的代码。

更安全的选择是使用安全调用运算符 ?.,如以下示例所示:

val account = Account("name", "type")
    val accountName = account.name?.trim()
    

使用安全调用运算符时,如果 name 不为 null,则 name?.trim() 的结果是一个不带前导或尾随空格的名称值。如果 name 为 null,则 name?.trim() 的结果为 null。这意味着,在执行此语句时,您的应用永远不会抛出 NullPointerException。

虽然安全调用运算符可使您避免潜在的 NullPointerException,但它会将 null 值传递给下一个语句。您可以使用 Elvis 运算符 (? : ) 来紧接着处理 null 值的情况,如以下示例所示:

val account = Account("name", "type")
    val accountName = account.name?.trim() ?: "Default name"
    

如果 Elvis 运算符左侧表达式的结果为 null,则会将右侧的值赋予 accountName。此方法对于提供本来为 null 的默认值很有用。

您还可以使用 Elvis 运算符提前从函数返回结果,如以下示例所示:

fun validateAccount(account: Account?) {
        val accountName = account?.name?.trim() ?: "Default name"

        // account cannot be null beyond this point
        account ?: return

        ...
    }
    

4、条件语句

Kotlin提供了几种用来实现条件逻辑的机制,其中最常见的是if-else语句。如果if关键字后面括在圆括号内的表达式求值为true,则会执行该分支中的代码,否则会执行else分支中的代码,和Java是一样的:

if(count == 42) {
	println("I have an apple.")
} else {
	println("I have a pen.")
}

您还可以使用else if 表示多个条件,和大多说编程语言都是一样的:

if(count == 42) {
	//条件分支1
	println("I have an apple.")
}else if (count > 35) {
//条件分支2
	pritln("I have a pen.")
}else {
//其他情况
	println("I have an apple pen.")
}

条件语句对于表示有状态的逻辑很有用,但您可能会发现,编写这些语句时会重复。在上面的示例中,每个分支输出一个String。为了避免这种重复,Kotlin提供了条件表达式。最后一个示例可以重新编写如下:

val answerString: String = if(count == 42) {
	//条件分支1
	"I have an apple."
}else if (count > 35) {
//条件分支2
	"I have a pen."
}else {
//其他情况
	"I have an apple pen."
}
println(answerString)

每个条件分支都隐式的返回它最后一行的表达式结果,因此不需要return关键字。因为每个分支的结果都是String类型,因此if-else表达式的结果也是String类型。在本例中,根据if-else表达式的结果赋值给answerString。可以利用类型推断来省略answerString的显示类型声明,但为了清楚起见,通常最好添加该声明。
注意:Kotlin不包含三元运算,而是倾向于使用条件表达式。
随着条件语句的复杂性不断增加,您可以考虑将if-else表达式替换为when表达式,如下示例:

val answerString = when {
	count == 42 -> "I have an apple."
	count > 35 -> "I have a pen."
	else -> "I have an apple pen."
}
println(answerString)

when 表达式中的每个分支都由一个条件、一个箭头(->) 和一个结果来表示。如果箭头左侧的条件值为true,则会返回右侧表达式的结果。请注意,执行并不是从一个分支跳转到下一个分支。when表达式的代码在功能上与上一个示例中的代码是等效的。
Kotlin的条件语句彰显了它的一项强大的功能,级智能类型转换。您不必使用安全调用运算符或非null断言运算符来处理可为null的值,而是可以使用条件语句来检查变量是否包含对null值的引用,如下所示:

val languageName: String? = null
if(languageName != null){
	//不需要写成languageName?.toUpperCase()
	println(languageName.toUpperCase())
}

在条件分支中,languageName可能被视为不可为null.Kotlin非常智能,能够明白执行分支的条件是languageName不为null值,因此您不必在该分支中将languageName当做可为null类型。这种智能类型转换适用于null检查、类型检查,或符合约定的任何条件。

5、函数

命名函数

Kotlin使用fun关键字来声明函数,格式如下:
fun funName(param: DataType) : returnDataType {
}

比如可以把上面例子封装成一个函数:

fun getAnswerString(): String {
	val answerString =  when {
		count == 42 -> "I have an apple."
		count > 35 -> "I have a pen."
		else -> "I have an apple pen."
	}
	return answerString
}

上面例子中的函数名称为"getAnswerString"。 它没有输入参数,且返回一个String类型的结果.函数的调用则跟Java是一样的:

val answerString = getAnswerString()

匿名函数

与命名函数一样,匿名函数也可以包含任意数量的表达式。函数的返回值是最终表达式的结果:

val stringLengthFunc: (String) -> Int = {input ->
	input.length
}

上面例子中,stringLengthFunc 的值是由其后面的匿名函数返回的,该匿名函数的入参类型是String,并将输入的String长度(为Int类型数据)返回。因此,该函数的类型表示为 (String) -> Int。 不过,此代码不会调用该函数。要检索该函数的结果,您必须像调用命名函数一样调用stingLengthFunc时,同时您必须提供String类型入参,如下:

val stringLength: Int = stringLengthFunc("Android")

高阶函数

一个函数可以将另一个函数当做参数,也就是说入参是函数的函数被称为“高阶函数”。有些语言中叫做回调函数,此模式对组件之间的通信(其方式与在Java中使用回调接口相同)非常有用。
下面是一个高阶函数的示例:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
	//调用函数
	return mapper(str)
}

上面示例中stringMapper() 函数有两个入参,分别是String类型和一个函数,传入的函数根据您传入的String来推导出一个Int值。要调用stringMapper()函数,您需要传入一个String和一个满足其他输入参数的函数(即入参为String返回值为Int的函数),如下示例:

stringMapper("Android", {input ->
	input.length
})

如果匿名函数是在某个函数的最后一个参数,您可以用于调用该函数的圆括号之外传入,上面的代码还可以写成如下方式:

stringMapper("Android") { input ->
	input.length
}

Kotlin标准库中的匿名函数还可以参阅Kotlin的官方文档- - Higher-Order Functions and Lambdas

6、类

到目前为止所提到的都是Kotlin中的内置类,如果想要添加自己的自定义类型,可以使用class关键字来定义类,使用关键字class和Java是类似的:

class MyKotlinTest

属性

属性是类级变量,可以包含getter、setter和后备字段,声明和Java语法相似:

class Car {
	val wheels = listOf<Wheel>()
}

请注意,wheels是一个public val,这意味着 ,可以从外部访问该变量,并且不能为其重新赋值。如果要获取Car的实例,必须要先调用构造函数。这样一来,就可以访问它的任何可访问的属性了,这一点也是跟Java有共通的地方的:

val car = Car() //  创建一个car实例
var wheels = car.wheels // 通过car实例调用Car类的wheels属性值

如果您想要在创建汽车实例的同时自定义汽车的轮子属性,您可以定义一个带参数的自定义构造函数,和Java也是一样的做法:

class Car(val wheels: List<Wheel>)

类函数和封装

类使用函数对行为建模。函数可以修改状态,从而帮您只公开希望公开的数据。这种访问控制机制属于面向对象的封装思想。由此,我们也能看出Kotlin和Java有很多的相似之处。
如下所示:

class Car(val wheels: List<Wheel>) {
	private val doorLock: DoorLock = ...
	fun unlockDoor(key: Key): Boolean {
		//如果key是有效的返回true,否则返回false
	}
}

上面的代码中,doorLock属性对Car类外部是不可见的,要解锁汽车就要通过调用unlockDorr(Key) 函数传入有效的Key。
如果您想要自定义属性的引用方式,则可以提供自定义的getter和setter。比如,您想要公开属性的getter而限制setter,则可以将该setter指定为private,如下:

class Car(var wheels: List<Wheel>) {
	private val doorLock: DoorLock = ...
	val gallonsOfFuelInTank: Int = 15
		private set
	fun unlockDoor(key: Key): Boolen{
		//如果钥匙有效则返回true,否则返回false
	}
}

通过类属性和类中函数的结合使用,您可以创建能够对所有类型的对象建模的类。

7、互操作性

Kotlin最重要的功能之一就是它与Java之间流畅的互操作性。由于Kotlin代码可编译为JVM字节码,因此Kotlin代码可以直接调用Java代码,反之亦然。这意味着,您可以直接用Kotlin调用现有的Java库。绝大多数的Android API都是用Java编写的,所以你可以直接使用Kotlin调用它们。

你还可以去如下的网址进行学习Kotlin:

  1. Android开发者官网 — 学习Kotlin语言
  2. Kotlin官网
  3. 在 Android 开发中使用常见的 Kotlin 模式
发布了37 篇原创文章 · 获赞 15 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/u012764358/article/details/105623694