本文將從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
流礁。可以顯示的增加后綴f
或F
表示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
- Long使用后綴
- 十六進制:
0x0F
- 二進制:
0b00001011
不支持八進制
Kotlin同樣支持浮點數(shù)的常規(guī)表示法:
- Double :
123.5
,123.5e10
- Float 使用后綴
f
orF
: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
anda != 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.0
比0.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
類型表示布爾值诚撵,只有true 和false 兩個值。當(dāng)需要一個布爾值的非空引用時键闺,布爾值也需要裝箱操作。
布爾值的內(nèi)置操作包括:
-
||
– 或 -
&&
– 與 -
!
- 非
數(shù)組
在Kotlin中Array
類表示數(shù)組澈驼,擁有get
和set
函數(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):
-
后綴
u
和U
標(biāo)記為無符號英古。 確切類型將根據(jù)預(yù)期類型推導(dǎo)淀衣。 如果未提供預(yù)期的類型,則將根據(jù)大小選擇UInt
或ULong
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
-
后綴
uL
和UL
表示無符號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)入的包有:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.* (since 1.1)
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
其他包的導(dǎo)入依賴目標(biāo)平臺:
- JVM:
- java.lang.*
- kotlin.jvm.*
- JS:
導(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ù)或擴展函數(shù):
這三種函數(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)
while 和do..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)的break 和continue 操作履因。
返回和跳轉(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")