Kotlin简介

136 阅读9分钟

基于谷歌文档翻译

声明变量

val and var

`val` 修饰的变量不可变,类似于final

`var` 修饰的变量可变

`var` count: `Int` = 10

修改变量的值: count = 15。

如果定义的变量不想再被更改,则可以用val修饰,如:

val languageName: String = "Kotlin"

类型引用

继续上门的例子,当初始化languageName时,Kotlin编译器能够根据分配的值进行类型推断,因为"Kotlin"是String类型,编译器推断languageName也是一个String。注意Kotlin是静态类型语言,也就意味着,类型解析发生在编译时,而且不会改变。在下面的例子中,languageName被对推断成一个String类型,所以不能调用除String类型方法以外的其他方法。Kotlin的类型推断方法可为您提供简洁性和类型安全性

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

空安全

在某些语言中,可以在不提供初始显式值的情况下声明引用类型变量。 在这些情况下,变量通常包含空值。 默认情况下,Kotlin变量不能包含空值。 这意味着以下代码段无效:

// Fails to compile
val languageName: String = null

要使变量具有空值,它必须是可空类型。 您可以通过在变量的类型后加上?来将其指定为可空值,如以下示例所示:

val languageName: String? = null

String? 类型,您可以将String值或null分配给languageName。

您必须谨慎处理可为空的变量,否则可能会引发可怕的NullPointerException。 例如,在Java中,如果尝试对空值调用方法,则程序将崩溃。

Kotlin提供了许多机制来安全处理可为空的变量

条件

Kotlin具有几种用于实现条件逻辑的机制。 其中最常见的是if-else语句。 如果if关键字旁边括号内的表达式的计算结果为true,则执行该分支内的代码(即紧接在花括号内的紧跟其后的代码)。 否则,将执行else分支内的代码。

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

您可以使用else if表示多个条件。 这使您可以在单个条件语句中表示更精细,更复杂的逻辑,如以下示例所示:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

条件语句对于表示状态逻辑很有用,但是您可能会发现编写它们时会重复自己。 在上面的示例中,您只需在每个分支中打印一个字符串。 为了避免这种重复,Kotlin提供了条件表达式。 最后一个示例可以重写如下:

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

隐式地,每个条件分支在其最后一行返回表达式的结果,因此您无需使用return关键字。 因为所有三个分支的结果均为String类型,所以if-else表达式的结果也为String类型。 在此示例中,根据if-else表达式的结果为answerString分配了初始值。 类型推断可用于省略answerString的显式类型声明,但为清楚起见通常将其包括在内是一个好主意。

注意:Kotlin不包括传统的三元运算符,而是倾向于使用条件表达式。

随着条件语句的复杂性增加,您可以考虑将if-else表达式替换为when表达式,如以下示例所示:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

when表达式中的每个分支均由条件,箭头(->)和结果表示。 如果箭头左侧的条件求值为true,则返回右侧表达式的结果。 请注意,执行不会从一个分支到下一个分支。 when表达式示例中的代码在功能上与先前示例中的代码等效,但是可以说更易于阅读。 Kotlin的附加条件突出了其更强大的功能之一,即智能casting。 您可以使用条件语句检查变量是否包含对空值的引用,而不是使用安全调用运算符或非空断言运算符来处理可空值,如以下示例所示:

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

在条件分支内,languageName可被视为不可为空。 Kotlin足够聪明,可以认识到执行分支的条件是languageName不包含null值,因此您不必在该分支内将languageName视为可为空。 这种智能转换适用于null检查,类型检查或满足合同的任何条件。

函数

您可以将一个或多个表达式分组为一个函数。 您可以将表达式包装在一个函数中,然后调用该函数,而不是每次需要结果时都重复相同的一系列表达式。

要声明函数,请使用fun关键字,后跟函数名称。 接下来,定义函数采用的输入类型(如果有),并声明其返回的输出类型。 函数的主体是您定义调用函数时要调用的表达式的地方。

在前面的示例的基础上,以下是完整的Kotlin函数:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

上例中的函数名称为generateAnswerString。 不需要任何输入。 它输出String类型的结果。 要调用函数,请使用其名称,后跟调用运算符(())。 在下面的示例中,将使用generateAnswerString()的结果初始化answerString变量。

val answerString = generateAnswerString()

函数可以将参数作为输入,如以下示例所示:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

声明函数时,可以指定任意数量的参数及其类型。 在上面的示例中,generateAnswerString()接受一个名为Int类型的countThreshold的参数。 在函数内,您可以使用参数名称来引用该参数。

调用此函数时,必须在函数调用的括号中包含一个参数:

val answerString = generateAnswerString(42)

简化函数声明

generateAnswerString()是一个相当简单的函数。 该函数声明一个变量,然后立即返回。 从函数返回单个表达式的结果时,可以通过直接返回函数中包含的if-else表达式的结果来跳过声明局部变量的步骤,如以下示例所示:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

还可以使用赋值运算符替换return关键字:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

匿名函数

并非每个功能都需要一个名称。 某些功能可以通过其输入和输出来直接识别。 这些功能称为匿名函数。 您可以保留对匿名函数的引用,以后使用该引用来调用匿名函数。 您还可以像其他引用类型一样在应用程序中传递引用。

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

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

在上面的示例中,stringLengthFunc包含对匿名函数的引用,该匿名函数以String作为输入,并返回输入String的长度作为Int类型的输出。 因此,该函数的类型表示为(String)-> Int。 但是,此代码不会调用该函数。 要检索该函数的结果,必须像命名函数一样调用它。 调用stringLengthFunc时必须提供String,如以下示例所示:

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

val stringLength: Int = stringLengthFunc("Android")

高阶函数

一个函数可以将另一个函数作为参数。 将其他函数用作参数的函数称为高阶函数。 该模式对于组件之间的通信很有用,就像在Java中使用回调接口一样。

这是一个高阶函数的示例:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

stringMapper()函数采用一个String以及一个函数,该函数从传递给它的String中得出一个Int值。

您可以通过传递一个String和一个满足其他输入参数的函数来调用stringMapper(),即,将String作为输入并输出一个Int的函数,如以下示例所示:

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

如果匿名函数是函数上定义的最后一个参数,则可以将其传递到用于调用该函数的括号之外,如以下示例所示:

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

到目前为止提到的所有类型都内置在Kotlin编程语言中。 如果要添加自己的自定义类型,则可以使用class关键字定义一个类,如以下示例所示:

class Car

属性

类使用属性表示状态。 属性是类级别的变量,可以包含getter,setter和backing字段。 由于汽车需要车轮来驱动,因此可以将Wheel对象列表添加为Car的属性,如以下示例所示:

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

请注意,wheels是一个公共val,这意味着可以从Car类的外部访问轮子,并且不能重新分配它。 如果要获取Car的实例,则必须首先调用其构造函数。 从那里,您可以访问其任何可访问的属性。

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

如果要自定义wheels,则可以定义一个自定义构造函数,该构造函数指定如何初始化类属性:

class Car(val wheels: List<Wheel>)

在上面的示例中,类构造函数将List 作为构造函数参数,并使用该参数初始化其wheel属性。

类函数和封装

类使用函数对行为进行建模。 函数可以修改状态,从而帮助您仅公开希望公开的数据。 此访问控制是称为封装的较大的面向对象概念的一部分。

在下面的示例中,doorLock属性对Car类之外的所有内容保持私有状态。 要解锁汽车,必须调用传入有效密钥的unlockDoor()函数,如以下示例所示:

  class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

如果要自定义如何引用属性,则可以提供自定义的getter和setter。 例如,如果您想在限制访问设置器的同时公开属性的getter,则可以将该设置器指定为私有:

class Car(val wheels: List<Wheel>) {

  private val doorLock: DoorLock = ...

  val gallonsOfFuelInTank: Int = 15
      private set

  fun unlockDoor(key: Key): Boolean {
      // Return true if key is valid for door lock, false otherwise
  }
}

通过属性和函数的组合,可以创建对所有类型的对象建模的类。

互通性

Kotlin最重要的功能之一是它与Java的流畅互操作性。 因为Kotlin代码可以编译为JVM字节码,所以您的Kotlin代码可以直接调用Java代码,反之亦然。 这意味着您可以直接从Kotlin利用现有的Java库。 此外,大多数Android API都是用Java编写的,您可以直接从Kotlin对其进行调用。