【Kotlin學(xué)習(xí)筆記】基礎(chǔ)知識

本文將從Kotlin的基本類型逗宜,打包和導(dǎo)入,流程控制,返回中斷和典型編碼風(fēng)格五個部分介紹Kotlin的基礎(chǔ)知識汽馋。

本文中所有代碼的運行平臺為: JVMRunning on kotlin v. 1.3.61

基本類型

在Kotlin中,我們可以在任何變量上調(diào)用成員方法和屬性圈盔,從這個意義上講Kotlin中一切皆是對象豹芯。一些類型有內(nèi)部實現(xiàn)形式,比如 數(shù)字驱敲,字符和布爾值在運行時適用基本類型表示铁蹈,但是可以像普通類一樣使用。在這一章我們一起學(xué)習(xí)Kotlin中的基本類型:數(shù)字众眨,字符握牧,布爾值,數(shù)組和字符串娩梨。

數(shù)字

Kotlin提供了多種內(nèi)置類型用于表示數(shù)字沿腰。對于整數(shù),有四種不同長度的內(nèi)置類型狈定,自然取值范圍也不同颂龙。

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

對于浮點數(shù)枫慷,Kotlin提供了單精度的Float 和雙精度的Double

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

使用小數(shù)初始化的變量包斑,編譯器會推斷變量的類型為Double 流礁。可以顯示的增加后綴fF 表示Float 類型罗丰。如果初始化值的小數(shù)部分超過6-7位神帅,數(shù)據(jù)會被四舍五入截斷。

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

和其它編程語言不同萌抵,數(shù)字在Kotlin中沒有隱式的加寬轉(zhuǎn)換找御。例如,一個使用Double 類型參數(shù)的函數(shù)绍填,不能使用Float霎桅,Int 或其他數(shù)據(jù)類型的值。

fun main(args: Array<String>) {
    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
}

字面常量

整數(shù)值有以下幾種字面常量:

  • 十進制: 123
    • Long使用后綴 L: 123L
  • 十六進制: 0x0F
  • 二進制: 0b00001011

不支持八進制

Kotlin同樣支持浮點數(shù)的常規(guī)表示法:

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

使用下劃線

1.1版本開始支持

可以使用下劃線增加數(shù)字可讀性:

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平臺上讨永,除非需要可為空的數(shù)字引用(例如Int滔驶?)或涉及泛型,否則數(shù)字實際上是作為JVM基本類型存儲的卿闹。后一種情況揭糕,數(shù)字需要裝箱。

數(shù)字裝箱沒有必要保留唯一性:

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'

顯示轉(zhuǎn)換

由于表示形式不同著角,較小的類型不是較大的子類型。 如果是這樣旋恼,我們將遇到以下麻煩:

// 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

因此相等性將無處不在吏口,更不用說唯一性了。

因此冰更,較小的類型不會隱式轉(zhuǎn)換為較大的類型产徊。這意味著如果不進行顯式轉(zhuǎn)換,則無法將Byte類型的值分配給Int變量.

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

可以使用顯示類型轉(zhuǎn)換:

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

每種數(shù)字類型都支持以下轉(zhuǎn)換:

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

隱式轉(zhuǎn)換的缺乏很少引起注意蜀细,因為類型是從上下文推斷出來的囚痴,并且算術(shù)運算對于適當(dāng)?shù)霓D(zhuǎn)換是重載的,例如

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

操作

Kotlin支持數(shù)字上的標(biāo)準(zhǔn)算術(shù)運算集审葬,這些算術(shù)運算被聲明為適當(dāng)類的成員(但編譯器會優(yōu)化對相應(yīng)指令的調(diào)用)深滚。

從位操作開始奕谭,它們沒有特殊字符,而只是可以以中綴形式調(diào)用的命名函數(shù)痴荐,例如:

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

浮點數(shù)比較

討論浮點數(shù)的下面三種運算:

  • 相等性檢查: a == b and a != b
  • 比較操作符: a < b, a > b, a <= b, a >= b
  • 范圍實例化和范圍檢查: a..b, x in a..b, x !in a..b

當(dāng)操作數(shù)a和b已知為Float或Double或它們的可為空的對應(yīng)對象(類型已聲明或推斷或是智能強制轉(zhuǎn)換的結(jié)果)時血柳,對數(shù)字和它們形成的范圍的運算遵循IEEE 754 浮點算法的標(biāo)準(zhǔn)。

但是,為了支持通用用例并提供總排序,當(dāng)未將操作數(shù)靜態(tài)類型化為浮點數(shù)時(例如贺拣,Any,Comparable <...>根吁,類型參數(shù)),操作將對Float和Float使用equals和compareTo實現(xiàn)合蔽。 Double击敌,它與標(biāo)準(zhǔn)不一致,因此:

  • NaN 和自身相等
  • NaN 比任何數(shù)都大
  • -0.00.0

字符

Char 類型表示字符拴事,Char 不能被直接當(dāng)作數(shù)字處理沃斤。

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

字符的字面量需要使用單引號引起來:‘1’。特殊字符需要使用反斜杠轉(zhuǎn)義刃宵。Kotlin支持以下轉(zhuǎn)義序列: \t, \b, \n, \r, \', \", \\ and \$衡瓶。如果要編碼任何字符,可以使用Unicode的轉(zhuǎn)義語法:'\uFF00'

可以顯示的將一個Char 轉(zhuǎn)換為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
}

和數(shù)字雷系哮针,當(dāng)需要一個非空字符引用時需要裝箱操作。裝箱操作不保證唯一性坦袍。

布爾值

Kotlin中的Boolean 類型表示布爾值诚撵,只有truefalse 兩個值。當(dāng)需要一個布爾值的非空引用時键闺,布爾值也需要裝箱操作。

布爾值的內(nèi)置操作包括:

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

數(shù)組

在Kotlin中Array 類表示數(shù)組澈驼,擁有getset 函數(shù)(通過運算符重載轉(zhuǎn)為[])辛燥,一個表示長度的屬性size 和一些其他有用的成員函數(shù):

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

    operator fun iterator(): Iterator<T>
    // ...
}

創(chuàng)建一個數(shù)組,可以使用kotlin的庫函數(shù)arrayOf 缝其,該函數(shù)支持任意長度的參數(shù)挎塌,因此可以使用不同個數(shù)的元素填充數(shù)組,例如内边,arrayOf(1,2,3) 創(chuàng)建了一個包含三個元素的數(shù)組[1榴都,2,3]漠其。如果想創(chuàng)建一個給定長度數(shù)組元素為空的數(shù)組可以使用arrayOfNulls() 函數(shù)嘴高。

創(chuàng)建數(shù)組的另一種方式是使用Array 構(gòu)造函數(shù)竿音,構(gòu)造函數(shù)接受一個數(shù)字表示數(shù)組長度和一個能返回指定索引值的函數(shù):

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

Kotlin中的數(shù)組是不可變的,這意味著不支持將一個Array 賦值給另一個Array拴驮,這阻止了運行時可能發(fā)生的錯誤春瞬。

基本類型數(shù)組

Kotlin有專門的類來表示基本類型的數(shù)組,而無需裝箱:ByteArray, ShortArray, IntArray 等套啤。這些類和Array 類沒有繼承關(guān)系宽气,但是有相同的方法集和屬性。每一個類都有一個對應(yīng)的工廠方法:

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  

無符號整數(shù)(實驗特性)

注意:無符號類型只在Kotlin 1.3 版本以后才支持潜沦,特性目前還處在實驗階段萄涯。

Kotlin 包含以下類型的無符號整數(shù):

  • kotlin.UByte: 8位無符號整數(shù), 取值范圍:[0,255]
  • kotlin.UShort: 16位無符號整數(shù), 取值范圍:[0唆鸡,65535]
  • kotlin.UInt: 32位無符號整數(shù)涝影,取值范圍:[0,2^32 - 1]
  • kotlin.ULong: 64位無符號整數(shù)喇闸,取值范圍:[0袄琳,2^64 - 1]

無符號和有符號類型之間的轉(zhuǎn)換是二進制不兼容的。

無符號類型使用另一個實驗功能(即內(nèi)聯(lián)類)實現(xiàn)燃乍。

特殊類

與基本類型相同唆樊,每個無符號類型都有對應(yīng)的表示數(shù)組的類型,專門用于該無符號類型:

  • kotlin.UByteArray: 無符號byte數(shù)組刻蟹。
  • kotlin.UShortArray: 無符號short數(shù)組逗旁。
  • kotlin.UIntArray: 無符號int數(shù)組。
  • kotlin.ULongArray: 無符號long數(shù)組舆瘪。

字面量

為了使無符號整數(shù)更易于使用片效,Kotlin提供了使用后綴標(biāo)記整數(shù)的功能,該后綴表示特定的無符號類型(類似于Float / Long):

  • 后綴uU標(biāo)記為無符號英古。 確切類型將根據(jù)預(yù)期類型推導(dǎo)淀衣。 如果未提供預(yù)期的類型,則將根據(jù)大小選擇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
    

無符合整數(shù)的實驗狀態(tài)

無符號類型的設(shè)計是實驗性的召调,這意味著此功能正在快速發(fā)展膨桥,并且不提供兼容性保證。在Kotlin 1.3+版本使用無符號計算式會收到警告唠叛,來表明這個特性處于實驗階段只嚣。要刪除警告,您必須選擇試用無符號類型艺沼。

可以選擇兩種方法來選擇無符號類型:也可以將API標(biāo)記為實驗性册舞,也可以不這樣做。

  • 可以使用@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有兩種類型的字符串字面量:可能包含轉(zhuǎn)義字符的轉(zhuǎn)義字符串和包含換行符和任意文本的原始字符串贯钩。下面是一個轉(zhuǎn)義字符串樣例:

val s = "Hello, world!\n"

轉(zhuǎn)義使用常規(guī)的反斜杠募狂,支持的轉(zhuǎn)義字符和字符 一節(jié)描述的一致。原始字符串使用兩對三個單引號劃分''''''角雷,不包含轉(zhuǎn)義符祸穷,并且可以包含換行符和任何其他字符:

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

使用trimMargin() 函數(shù)可以刪除字符串前面的空格:

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

默認使用| 作為邊前綴,你可以選擇其他的字符并將該字符傳遞給函數(shù)trimMargin勺三。

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

字符串模板

字符串可能包含模板表達式雷滚,即經(jīng)過計算并將其結(jié)果拼接到字符串中的代碼段。模板表達式以$開始吗坚,并包含一個簡單名稱:

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

或是花括號{}中的任意表達式:

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

原始字符串和轉(zhuǎn)義字符串均支持模板祈远,如果需要在原始字符串中表示一個$ 可以使用下面的表達式:

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

包和導(dǎo)入

Kotlin的的源文件可以以打包聲明開始:

package org.example

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

// ...

源文件的所有內(nèi)容(比如類和函數(shù))都包含在聲明的包中。因此商源,在上面的例子中printMessage() 函數(shù)的全名為org.example.printMessage 车份,類Message 的全名為org.example.Message。如果沒有聲明包牡彻,源文件的所有內(nèi)容屬于默認的包(默認包沒有名字)扫沼。

默認導(dǎo)入

每個Kotlin源文件默認導(dǎo)入的包有:

其他包的導(dǎo)入依賴目標(biāo)平臺:

導(dǎo)入

除了默認導(dǎo)入,每個文件可能包含自己的導(dǎo)入指令庄吼。導(dǎo)入的語法和Java中類似缎除,但是又有一些不同,具體的語法可以參考grammar.

導(dǎo)入一個名字:

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

導(dǎo)入可見范圍內(nèi)的所有內(nèi)容:

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 關(guān)鍵字不僅可以導(dǎo)入類器罐,還可以導(dǎo)入其他聲明:

  • 頂層函數(shù)和屬性。
  • 單例中的函數(shù)和屬性渐行。
  • 枚舉常量轰坊。

頂層聲明的可見性

如果頂層聲明為private,那么這個聲明只對聲明所在文件可見殊轴。

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

If 表達式

在Kotlin中,if 是可以返回值的表達式袒炉,因此Kotlin沒有三目運算發(fā)旁理,因為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 支持使用代碼塊作為分支體我磁,在這種情況下孽文,代碼塊中的最后一個表達式就是結(jié)果:

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

如果把if 當(dāng)作表達式而不是語句驻襟,表達式哎喲求必須有一個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 既可以當(dāng)作表達式也可以當(dāng)作語句使用。如果when當(dāng)作表達式使用减牺,匹配條件分支的值就是整個when表達式的值豌习。如果when當(dāng)作語句使用,每個分支的值將被忽略拔疚。

如果所有的分支都不滿足條件肥隆,else分支就會被執(zhí)行。如果when用作表達式稚失,else分支是強制的栋艳,除非編譯器可以證明分支已經(jīng)包括了所有的條件。

如果多個分支的邏輯一樣句各,可以使用逗號將多個分支合并為一個分支:

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")
}

另外可能是檢查一個值是否是目標(biāo)類型吸占,受益于智能轉(zhuǎn)換,可以直接訪問方法和屬性而不用額外的檢查和轉(zhuǎn)換凿宾。

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

when也可以作為if-else-if 鏈的一種替代實現(xiàn)矾屯。如果沒有提供參數(shù),分支條件就是簡單的布爾表達式菌湃,如果條件為真則執(zhí)行對應(yīng)的分支:

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內(nèi)可見惧所。

For 循環(huán)

for 循環(huán)可以遍歷提供iterator的一切事務(wù)骤坐,和C#中的foreach 類似。

for (item in collection) print(item)

循環(huán)體可以為空:

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

上面提到的一樣下愈,for 循環(huán)可以遍歷提供iterator的一切纽绍。比如:

  • 擁有一個成員函數(shù)或擴展函數(shù)iterator()

    , iterator() 函數(shù)滿足以下兩點:

    • 擁有一個成員函數(shù)或擴展函數(shù):next() 返回一個對象或值。
    • 擁有一個成員函數(shù)或擴展函數(shù): hasNext() 返回 Boolean.

這三種函數(shù)都不要被標(biāo)記為 operator.

使用范圍表達式可以遍歷范圍內(nèi)的所有數(shù)字:

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

使用for循環(huán)遍歷一個范圍或數(shù)組势似,編譯器會將代碼編譯為基于索引的遍歷而不會創(chuàng)建iterator對象拌夏。

使用索引遍歷數(shù)組或列表的一種方式如下:

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

一種替代方法是使用 withIndex 庫函數(shù):

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

While 循環(huán)

whiledo..while 和Java中一樣:

while (x > 0) {
    x--
}

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

跳出和繼續(xù)循環(huán)

在循環(huán)中,Kotlin支持傳統(tǒng)的breakcontinue 操作履因。

返回和跳轉(zhuǎn)

Kotlin有三種結(jié)構(gòu)跳轉(zhuǎn)表達式:

  • return. 默認從最近的封閉函數(shù)或匿名函數(shù)返回障簿。
  • break. 終止最近的循環(huán)。
  • continue. 執(zhí)行最近循環(huán)的下一步栅迄。

上面三種表達式可以作為一個較大表達式的一部分:

val s = person.name ?: return

三種跳轉(zhuǎn)表達式的類型是無類型站故。

中斷和繼續(xù)標(biāo)簽

在Kotlin中所有的表達式都可以通過標(biāo)簽標(biāo)記。標(biāo)簽由一個標(biāo)識符+@ 構(gòu)成,標(biāo)記一個表達式只需要在表達式前添加一個標(biāo)簽西篓。

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

有了標(biāo)簽后愈腾,可以通過標(biāo)簽來中斷或繼續(xù):

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

帶有標(biāo)簽限定符的break將在標(biāo)記有該標(biāo)簽的循環(huán)之后立即跳轉(zhuǎn)到執(zhí)行點,continue進行到該循環(huán)的下一個迭代岂津。

在標(biāo)簽處返回

Kotlin中的局部函數(shù)和對象表達式可以嵌套使用虱黄,return 支持從外部函數(shù)返回,比較有用的一個場景可能是從lambda表達式返回吮成。

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

return表達式從最近的封閉函數(shù)返回橱乱,比如上面的foo()函數(shù)。如果想從一個lambda表達式返回赁豆,首先需要給lambda表達式一個標(biāo)簽仅醇,然后使用return+標(biāo)簽返回。

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")
}

現(xiàn)在魔种,它僅從lambda表達式返回析二。 通常,使用隱式標(biāo)簽更為方便:這樣的標(biāo)簽與lambda傳遞給的函數(shù)同名节预。

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")
}

或者叶摄,我們可以使用匿名函數(shù)替代lambda表達式,匿名函數(shù)種的return語句將從匿名函數(shù)自身返回安拟。

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類似于在常規(guī)循環(huán)中使用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 標(biāo)簽返回值1:

return@a 1

典型風(fēng)格

下面是Kotlin中慣用的編碼風(fēng)格会傲。

創(chuàng)建 DTOs (POJOs/POCOs)

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

Customer 提供以下函數(shù):

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

函數(shù)參數(shù)默認值

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

過濾list

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

檢查元素是否在集合中

if ("john@example.com" in emailsList) { ... }

if ("jane@example.com" !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
}

擴展函數(shù)

fun String.spaceToCamelCase() { ... }

"Convert this to camelcase".spaceToCamelCase()

創(chuàng)建單例

object Resource {
    val name = "Name"
}

非空速‘記’

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

println(files?.size)

空非空速'記'

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

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

如果為空執(zhí)行一個表達式

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

獲取集合第一個元素

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

非空則執(zhí)行

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"
    }
}

構(gòu)建器風(fēng)格方法Unit

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

單表達式函數(shù)

fun theAnswer() = 42

上面函數(shù)和下面的等價:

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")
}

調(diào)用一個實例的多個方法

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())
}

需要泛型信息的泛型函數(shù)的簡潔方法

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

inline fun <reified T: Any> 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(): 標(biāo)記代碼未完成

Kotlin標(biāo)準(zhǔn)庫中的TODO() 函數(shù)始終拋出一個NotImplementedError 錯誤淌山,它有一個接受原因作為參數(shù)的重載函數(shù)。

fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顾瞻,一起剝皮案震驚了整個濱河市泼疑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荷荤,老刑警劉巖退渗,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蕴纳,居然都是意外死亡会油,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門古毛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翻翩,“玉大人,你說我怎么就攤上這事√逭叮” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵颖低,是天一觀的道長絮吵。 經(jīng)常有香客問我,道長忱屑,這世上最難降的妖魔是什么蹬敲? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮莺戒,結(jié)果婚禮上伴嗡,老公的妹妹穿的比我還像新娘。我一直安慰自己从铲,他們只是感情好瘪校,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著名段,像睡著了一般阱扬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伸辟,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天麻惶,我揣著相機與錄音,去河邊找鬼信夫。 笑死窃蹋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的静稻。 我是一名探鬼主播警没,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姊扔!你這毒婦竟也來了惠奸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤恰梢,失蹤者是張志新(化名)和其女友劉穎佛南,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嵌言,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡嗅回,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摧茴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绵载。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娃豹,到底是詐尸還是另有隱情焚虱,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布懂版,位于F島的核電站鹃栽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躯畴。R本人自食惡果不足惜民鼓,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓬抄。 院中可真熱鬧丰嘉,春花似錦、人聲如沸嚷缭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阅爽。三九已至克滴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間优床,已是汗流浹背劝赔。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胆敞,地道東北人着帽。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像移层,于是被迫代替她去往敵國和親仍翰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容