【Kotlin学习笔记】基础知识

本文将从Kotlin的基本类型,打包和导入,流程控制,返回中断和典型编码风格五个部分介绍Kotlin的基础知识。

本文中所有代码的运行平台为: JVMRunning on kotlin v. 1.3.61

基本类型

在Kotlin中,我们可以在任何变量上调用成员方法和属性,从这个意义上讲Kotlin中一切皆是对象。一些类型有内部实现形式,比如 数字,字符和布尔值在运行时适用基本类型表示,但是可以像普通类一样使用。在这一章我们一起学习Kotlin中的基本类型:数字,字符,布尔值,数组和字符串。

数字

Kotlin提供了多种内置类型用于表示数字。对于整数,有四种不同长度的内置类型,自然取值范围也不同。

Type Size(bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 - 1)
Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 - 1)

所有变量的初始化值不超过Int的最大值时,变量的类型就是Int。如果初始值超过了Int 的最大值,变量的类型就是Long 。可以通过在初始值后添加L 来表明类型为Long

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

对于浮点数,Kotlin提供了单精度的Float 和双精度的Double

Type Size(bits) Significant bits Exponent bits Decimal digits
Float 32 24 8 6-7
Double 64 53 11 15-16

使用小数初始化的变量,编译器会推断变量的类型为Double 。可以显示的增加后缀fF 表示Float 类型。如果初始化值的小数部分超过6-7位,数据会被四舍五入截断。

val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817

和其它编程语言不同,数字在Kotlin中没有隐式的加宽转换。例如,一个使用Double 类型参数的函数,不能使用FloatInt 或其他数据类型的值。

fun main(args: Array) {
    fun printDouble(d: Double) { print(d) }

    val i = 1
    val d = 1.1
    val f = 1.1f

    printDouble(d)
//    printDouble(i) // Error: Type mismatch
//    printDouble(f) // Error: Type mismatch
}

字面常量

整数值有以下几种字面常量:

  • 十进制: 123
    • Long使用后缀 L: 123L
  • 十六进制: 0x0F
  • 二进制: 0b00001011

不支持八进制

Kotlin同样支持浮点数的常规表示法:

  • Double : 123.5, 123.5e10
  • Float 使用后缀 f or F: 123.5f

使用下划线

1.1版本开始支持

可以使用下划线增加数字可读性:

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

表示方法

在Java平台上,除非需要可为空的数字引用(例如Int?)或涉及泛型,否则数字实际上是作为JVM基本类型存储的。后一种情况,数字需要装箱。

数字装箱没有必要保留唯一性:

val a: Int = 10000
println(a === a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA) // !!!Prints 'false'!!!

另一方面,也保留了相等性:

val a: Int = 10000
println(a == a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA == anotherBoxedA) // Prints 'true'

显示转换

由于表示形式不同,较小的类型不是较大的子类型。 如果是这样,我们将遇到以下麻烦:

// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(b == a) // Surprise! This prints "false" as Long's equals() checks whether the other is Long as well

因此相等性将无处不在,更不用说唯一性了。

因此,较小的类型不会隐式转换为较大的类型。这意味着如果不进行显式转换,则无法将Byte类型的值分配给Int变量.

val b: Byte = 1 // OK, literals are checked statically
val i: Int = b // ERROR

可以使用显示类型转换:

val i: Int = b.toInt() // OK: explicitly widened
print(i)

每种数字类型都支持以下转换:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

隐式转换的缺乏很少引起注意,因为类型是从上下文推断出来的,并且算术运算对于适当的转换是重载的,例如

val l = 1L + 3 // Long + Int => Long

操作

Kotlin支持数字上的标准算术运算集,这些算术运算被声明为适当类的成员(但编译器会优化对相应指令的调用)。

从位操作开始,它们没有特殊字符,而只是可以以中缀形式调用的命名函数,例如:

val x = (1 shl 2) and 0x000FF000

按位运算的完整列表(仅适用于Int和Long):

  • shl(bits) – signed shift left
  • shr(bits) – signed shift right
  • ushr(bits) – unsigned shift right
  • and(bits) – bitwise and
  • or(bits) – bitwise or
  • xor(bits) – bitwise xor
  • inv() – bitwise inversion

浮点数比较

讨论浮点数的下面三种运算:

  • 相等性检查: a == b and a != b
  • 比较操作符: a < b, a > b, a <= b, a >= b
  • 范围实例化和范围检查: a..b, x in a..b, x !in a..b

当操作数a和b已知为Float或Double或它们的可为空的对应对象(类型已声明或推断或是智能强制转换的结果)时,对数字和它们形成的范围的运算遵循IEEE 754 浮点算法的标准。

但是,为了支持通用用例并提供总排序,当未将操作数静态类型化为浮点数时(例如,Any,Comparable <...>,类型参数),操作将对Float和Float使用equals和compareTo实现。 Double,它与标准不一致,因此:

  • NaN 和自身相等
  • NaN 比任何数都大
  • -0.00.0

字符

Char 类型表示字符,Char 不能被直接当作数字处理。

fun check(c: Char) {
    if (c == 1) { // ERROR: incompatible types
        // ...
    }
}

字符的字面量需要使用单引号引起来:‘1’。特殊字符需要使用反斜杠转义。Kotlin支持以下转义序列: \t, \b, \n, \r, \', \", \\ and \$。如果要编码任何字符,可以使用Unicode的转义语法:'\uFF00'

可以显示的将一个Char 转换为Int 类型。

fun decimalDigitValue(c: Char): Int {
    if (c !in '0'..'9')
        throw IllegalArgumentException("Out of range")
    return c.toInt() - '0'.toInt() // Explicit conversions to numbers
}

和数字雷系,当需要一个非空字符引用时需要装箱操作。装箱操作不保证唯一性。

布尔值

Kotlin中的Boolean 类型表示布尔值,只有truefalse 两个值。当需要一个布尔值的非空引用时,布尔值也需要装箱操作。

布尔值的内置操作包括:

  • || – 或
  • && – 与
  • ! - 非

数组

在Kotlin中Array 类表示数组,拥有getset 函数(通过运算符重载转为[]),一个表示长度的属性size 和一些其他有用的成员函数:

class Array private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit

    operator fun iterator(): Iterator
    // ...
}

创建一个数组,可以使用kotlin的库函数arrayOf ,该函数支持任意长度的参数,因此可以使用不同个数的元素填充数组,例如,arrayOf(1,2,3) 创建了一个包含三个元素的数组[1,2,3]。如果想创建一个给定长度数组元素为空的数组可以使用arrayOfNulls() 函数。

创建数组的另一种方式是使用Array 构造函数,构造函数接受一个数字表示数组长度和一个能返回指定索引值的函数:

val  arr = Array(5){i -> 2*i }
arr.forEach { print("$it ") }
println()
for (i in 0..4) {
    print("${arr[i]} ")
}

Kotlin中的数组是不可变的,这意味着不支持将一个Array 赋值给另一个Array,这阻止了运行时可能发生的错误。

基本类型数组

Kotlin有专门的类来表示基本类型的数组,而无需装箱:ByteArray, ShortArray, IntArray 等。这些类和Array 类没有继承关系,但是有相同的方法集和属性。每一个类都有一个对应的工厂方法:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
x.forEach { print("$it  ") }
println()

val arr1 = IntArray(5)
arr1.forEach { print("$it  ") }
println()

val arr2 = IntArray(5) { 42 }
arr2.forEach { print("$it  ") }
println()

var arr3 = IntArray(5) { it * 1 }
arr3.forEach { print("$it  ") }

上述代码的输出如下:

5  2  3  
0  0  0  0  0  
42  42  42  42  42  
0  1  2  3  4  

无符号整数(实验特性)

注意:无符号类型只在Kotlin 1.3 版本以后才支持,特性目前还处在实验阶段。

Kotlin 包含以下类型的无符号整数:

  • kotlin.UByte: 8位无符号整数, 取值范围:[0,255]
  • kotlin.UShort: 16位无符号整数, 取值范围:[0,65535]
  • kotlin.UInt: 32位无符号整数,取值范围:[0,2^32 - 1]
  • kotlin.ULong: 64位无符号整数,取值范围:[0,2^64 - 1]

无符号和有符号类型之间的转换是二进制不兼容的。

无符号类型使用另一个实验功能(即内联类)实现。

特殊类

与基本类型相同,每个无符号类型都有对应的表示数组的类型,专门用于该无符号类型:

  • kotlin.UByteArray: 无符号byte数组。
  • kotlin.UShortArray: 无符号short数组。
  • kotlin.UIntArray: 无符号int数组。
  • kotlin.ULongArray: 无符号long数组。

字面量

为了使无符号整数更易于使用,Kotlin提供了使用后缀标记整数的功能,该后缀表示特定的无符号类型(类似于Float / Long):

  • 后缀uU标记为无符号。 确切类型将根据预期类型推导。 如果未提供预期的类型,则将根据大小选择UIntULong

    val b: UByte = 1u  // UByte, expected type provided
    val s: UShort = 1u // UShort, expected type provided
    val l: ULong = 1u  // ULong, expected type provided
    
    val a1 = 42u // UInt: no expected type provided, constant fits in UInt
    val a2 = 0xFFFF_FFFF_FFFFu // ULong: no expected type provided, constant doesn't fit in UInt
    
  • 后缀uLUL 表示无符号long类型:

    val a = 1UL
    

无符合整数的实验状态

无符号类型的设计是实验性的,这意味着此功能正在快速发展,并且不提供兼容性保证。在Kotlin 1.3+版本使用无符号计算式会收到警告,来表明这个特性处于实验阶段。要删除警告,您必须选择试用无符号类型。

可以选择两种方法来选择无符号类型:也可以将API标记为实验性,也可以不这样做。

  • 可以使用@ExperimentalUnsignedTypes 或编译时使用-Xexperimental=kotlin.ExperimentalUnsignedTypes 来传递功能的实验性。
  • 使用 @UseExperimental(ExperimentalUnsignedTypes::class) 或编译时使用 -Xuse-experimental=kotlin.ExperimentalUnsignedTypes可以不传播实验性。

字符串

Kotlin中String 类用来表示字符串,字符串是不可变的。字符串的元素是字符并可以使用索引操作s[i] 访问。一个字符串可以使用for-loop 遍历:

val words = "Hello Kotlin";
for (c in words) {
    print(c)
}

可以使用+ 操作符拼接两个字符串,这同样适用于字符串和其他类型拼接,只要表达式中的第一个元素是字符串:

val s = "abc" + 1
println(s + "def")

字符串字面量

Kotlin有两种类型的字符串字面量:可能包含转义字符的转义字符串和包含换行符和任意文本的原始字符串。下面是一个转义字符串样例:

val s = "Hello, world!\n"

转义使用常规的反斜杠,支持的转义字符和字符 一节描述的一致。原始字符串使用两对三个单引号划分'''''',不包含转义符,并且可以包含换行符和任何其他字符:

val text = """
    for (c in "foo")
        print(c)
"""

使用trimMargin() 函数可以删除字符串前面的空格:

val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

默认使用| 作为边前缀,你可以选择其他的字符并将该字符传递给函数trimMargin

val text = """
>Tell me and I forget.
>Teach me and I remember.
>Involve me and I learn.
>(Benjamin Franklin)
""".trimMargin(">")
println(text)

字符串模板

字符串可能包含模板表达式,即经过计算并将其结果拼接到字符串中的代码段。模板表达式以$开始,并包含一个简单名称:

val i = 10
println("i = $i") // prints "i = 10"

或是花括号{}中的任意表达式:

val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"

原始字符串和转义字符串均支持模板,如果需要在原始字符串中表示一个$ 可以使用下面的表达式:

val price = """
${'$'}9.99
"""

包和导入

Kotlin的的源文件可以以打包声明开始:

package org.example

fun printMessage() { /*...*/ }
class Message { /*...*/ }

// ...

源文件的所有内容(比如类和函数)都包含在声明的包中。因此,在上面的例子中printMessage() 函数的全名为org.example.printMessage ,类Message 的全名为org.example.Message。如果没有声明包,源文件的所有内容属于默认的包(默认包没有名字)。

默认导入

每个Kotlin源文件默认导入的包有:

  • kotlin.*
  • kotlin.annotation.*
  • kotlin.collections.*
  • kotlin.comparisons.* (since 1.1)
  • kotlin.io.*
  • kotlin.ranges.*
  • kotlin.sequences.*
  • kotlin.text.*

其他包的导入依赖目标平台:

  • JVM:
    • java.lang.*
    • kotlin.jvm.*
  • JS:
    • kotlin.js.*

导入

除了默认导入,每个文件可能包含自己的导入指令。导入的语法和Java中类似,但是又有一些不同,具体的语法可以参考grammar.

导入一个名字:

import org.example.Message // Message is now accessible without qualification

导入可见范围内的所有内容:

import org.example.* // everything in 'org.example' becomes accessible

如果有名字冲突,可以使用as 重命名:

import org.example.Message // Message is accessible
import org.test.Message as testMessage // testMessage stands for 'org.test.Message'

import 关键字不仅可以导入类,还可以导入其他声明:

  • 顶层函数和属性。
  • 单例中的函数和属性。
  • 枚举常量。

顶层声明的可见性

如果顶层声明为private,那么这个声明只对声明所在文件可见。

流程控制: if, when, for, while

If 表达式

在Kotlin中,if 是可以返回值的表达式,因此Kotlin没有三目运算发,因为if 本身可以很好的完成三目运算符的工作。

var max = a 
if (a < b) max = b

var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// if作为一个表达式
val max = if (a > b) a else b

if 支持使用代码块作为分支体,在这种情况下,代码块中的最后一个表达式就是结果:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

如果把if 当作表达式而不是语句,表达式哎哟求必须有一个else 分支。

When 表达式

when 用于取代Java中的switch,一个简单的样例如下:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}

when 按照顺序依次检查所有分支直到分支满足条件。when 既可以当作表达式也可以当作语句使用。如果when当作表达式使用,匹配条件分支的值就是整个when表达式的值。如果when当作语句使用,每个分支的值将被忽略。

如果所有的分支都不满足条件,else分支就会被执行。如果when用作表达式,else分支是强制的,除非编译器可以证明分支已经包括了所有的条件。

如果多个分支的逻辑一样,可以使用逗号将多个分支合并为一个分支:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

可以使用任意的表达式作为分支条件:

when (x) {
    parseInt(s) -> print("s encodes x")
    else -> print("s does not encode x")
}

可以检查一个元素是否在集合中作为条件:

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

另外可能是检查一个值是否是目标类型,受益于智能转换,可以直接访问方法和属性而不用额外的检查和转换。

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

when也可以作为if-else-if 链的一种替代实现。如果没有提供参数,分支条件就是简单的布尔表达式,如果条件为真则执行对应的分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

从Kotlin 1.3版本开始,使用下面的语法捕获when中的变量成为可能:

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

when 定义的变量,只能在when body内可见。

For 循环

for 循环可以遍历提供iterator的一切事务,和C#中的foreach 类似。

for (item in collection) print(item)

循环体可以为空:

for (item: Int in ints) {
    // ...
}

上面提到的一样,for 循环可以遍历提供iterator的一切。比如:

  • 拥有一个成员函数或扩展函数iterator()

    , iterator() 函数满足以下两点:

    • 拥有一个成员函数或扩展函数:next() 返回一个对象或值。
    • 拥有一个成员函数或扩展函数: hasNext() 返回 Boolean.

这三种函数都不要被标记为 operator.

使用范围表达式可以遍历范围内的所有数字:

for (i in 1..3) {
    println(i)
}
for (i in 6 downTo 0 step 2) {
    println(i)
}

使用for循环遍历一个范围或数组,编译器会将代码编译为基于索引的遍历而不会创建iterator对象。

使用索引遍历数组或列表的一种方式如下:

for (i in array.indices) {
    println(array[i])
}

一种替代方法是使用 withIndex 库函数:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

While 循环

whiledo..while 和Java中一样:

while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // y is visible here!

跳出和继续循环

在循环中,Kotlin支持传统的breakcontinue 操作。

返回和跳转

Kotlin有三种结构跳转表达式:

  • return. 默认从最近的封闭函数或匿名函数返回。
  • break. 终止最近的循环。
  • continue. 执行最近循环的下一步。

上面三种表达式可以作为一个较大表达式的一部分:

val s = person.name ?: return

三种跳转表达式的类型是无类型。

中断和继续标签

在Kotlin中所有的表达式都可以通过标签标记。标签由一个标识符+@ 构成,标记一个表达式只需要在表达式前添加一个标签。

loop@ for (i in 1..100) {
    // ...
}

有了标签后,可以通过标签来中断或继续:

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop
    }
}

带有标签限定符的break将在标记有该标签的循环之后立即跳转到执行点,continue进行到该循环的下一个迭代。

在标签处返回

Kotlin中的局部函数和对象表达式可以嵌套使用,return 支持从外部函数返回,比较有用的一个场景可能是从lambda表达式返回。

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return // 从foo()函数返回
        print(it)
    }
    println("this point is unreachable")
}

return表达式从最近的封闭函数返回,比如上面的foo()函数。如果想从一个lambda表达式返回,首先需要给lambda表达式一个标签,然后使用return+标签返回。

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@{
        if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with explicit label")
}

现在,它仅从lambda表达式返回。 通常,使用隐式标签更为方便:这样的标签与lambda传递给的函数同名。

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with implicit label")
}

或者,我们可以使用匿名函数替代lambda表达式,匿名函数种的return语句将从匿名函数自身返回。

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
        if (value == 3) return  // local return to the caller of the anonymous fun, i.e. the forEach loop
        print(value)
    })
    print(" done with anonymous function")
}

请注意,在前三个示例中使用局部return类似于在常规循环中使用continue。对于break 没有直接等价语法,但是可以通过添加一个lambda表达式和非本地返回来模拟:

fun foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

下面的表达式表示在@a 标签返回值1:

return@a 1

典型风格

下面是Kotlin中惯用的编码风格。

创建 DTOs (POJOs/POCOs)

data class Customer(val name: String, val email: String)

Customer 提供以下函数:

  • 所有属性提供getters方法
  • 可变属性提供setter方法
  • equals()
  • hashCode()
  • toString()
  • copy()
  • component1(), component2(), …, 所有属性

函数参数默认值

fun foo(a: Int = 0, b: String = "") { ... }

过滤list

val positives = list.filter { x -> x > 0 }
val positives = list.filter { it > 0 }

检查元素是否在集合中

if ("[email protected]" in emailsList) { ... }

if ("[email protected]" !in emailsList) { ... }

字符串插值

println("Name $name")

实例检查

when (x) {
    is Foo -> ...
    is Bar -> ...
    else   -> ...
}

遍历map

for ((k, v) in map) {
    println("$k -> $v")
}

k, v 可以使用其他任何字符替代。

使用范围

for (i in 1..100) { ... }  // closed range: includes 100
for (i in 1 until 100) { ... } // half-open range: does not include 100
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
if (x in 1..10) { ... }

只读 list

val list = listOf("a", "b", "c")

只读 map

val map = mapOf("a" to 1, "b" to 2, "c" to 3)

访问 map

println(map["key"])
map["key"] = value

“懒属性”

val p: String by lazy {
    // compute the string
}

扩展函数

fun String.spaceToCamelCase() { ... }

"Convert this to camelcase".spaceToCamelCase()

创建单例

object Resource {
    val name = "Name"
}

非空速‘记’

val files = File("Test").listFiles()

println(files?.size)

空非空速'记'

val files = File("Test").listFiles()

println(files?.size ?: "empty")

如果为空执行一个表达式

val values = ...
val email = values["email"] ?: throw IllegalStateException("Email is missing!")

获取集合第一个元素

val emails = ... // might be empty
val mainEmail = emails.firstOrNull() ?: ""

非空则执行

val value = ...

value?.let {
    ... // execute this block if not null
}

获取map值,如果为空返回默认值

val value = ...

val mapped = value?.let { transformValue(it) } ?: defaultValue 
// defaultValue is returned if the value or the transform result is null.

使用when表达式返回值

fun transform(color: String): Int {
    return when (color) {
        "Red" -> 0
        "Green" -> 1
        "Blue" -> 2
        else -> throw IllegalArgumentException("Invalid color param value")
    }
}

'try/catch' 表达式

fun test() {
    val result = try {
        count()
    } catch (e: ArithmeticException) {
        throw IllegalStateException(e)
    }

    // Working with result
}

'if' 表达式

fun foo(param: Int) {
    val result = if (param == 1) {
        "one"
    } else if (param == 2) {
        "two"
    } else {
        "three"
    }
}

构建器风格方法Unit

fun arrayOfMinusOnes(size: Int): IntArray {
    return IntArray(size).apply { fill(-1) }
}

单表达式函数

fun theAnswer() = 42

上面函数和下面的等价:

fun theAnswer(): Int {
    return 42
}

下面是一个和其他方法一起使用以减少代码的样例:

fun transform(color: String): Int = when (color) {
    "Red" -> 0
    "Green" -> 1
    "Blue" -> 2
    else -> throw IllegalArgumentException("Invalid color param value")
}

调用一个实例的多个方法

class Turtle {
    fun penDown()
    fun penUp()
    fun turn(degrees: Double)
    fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) { //draw a 100 pix square
    penDown()
    for(i in 1..4) {
        forward(100.0)
        turn(90.0)
    }
    penUp()
}

配置对象属性

val myRectangle = Rectangle().apply {
    length = 4
    breadth = 5
    color = 0xFAFAFA
}

Java 7 中的try with resources

val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
    println(reader.readText())
}

需要泛型信息的泛型函数的简洁方法

//  public final class Gson {
//     ...
//     public  T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException {
//     ...

inline fun  Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)

消费非空布尔值

val b: Boolean? = ...
if (b == true) {
    ...
} else {
    // `b` is false or null
}

交换两个变量值

var a = 1
var b = 2
a = b.also { b = a }

TODO(): 标记代码未完成

Kotlin标准库中的TODO() 函数始终抛出一个NotImplementedError 错误,它有一个接受原因作为参数的重载函数。

fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")

你可能感兴趣的:(【Kotlin学习笔记】基础知识)