本文同步更新于旺仔的個人博客,訪問可能有點慢,多刷新幾次。
Kotlin中有一些常用的關(guān)鍵字和標(biāo)識符只壳,同時還有一些操作符和特殊符號俏拱,這些都是和Java有不一樣的地方的暑塑,這里將他們介紹一下,方便記憶和回看锅必。
硬關(guān)鍵字(Hard Keywords)
Kotlin中的硬關(guān)鍵字不能作為標(biāo)識符
package
與Java一樣事格,Kotlin的源文件同樣以包聲明開始的。
package foo.bar
fun baz() {}
class Goo {}
// ...
interface
interface
表示聲明一個接口搞隐,
interface MyInterface {
fun bar()
fun foo() {
// 可選的方法體
}
}
class
Kotlin的類的聲明與Java一樣驹愚,使用class
關(guān)鍵字
class Invoice {
}
object
object
為同時聲明一個類及其實例,請看對象表達式劣纲。
super
具體內(nèi)容可看Kotlin學(xué)習(xí)_類和繼承逢捺、接口與實現(xiàn)。
引用一個方法或?qū)傩缘某悓崿F(xiàn)
open class Foo {
open fun f() { println("Foo.f()") }
open val x: Int get() = 1
}
class Bar : Foo() {
override fun f() {
super.f()
println("Bar.f()")
}
override val x: Int get() = super.x + 1
}
在此構(gòu)造函數(shù)中調(diào)用超類構(gòu)造函數(shù)
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
null
null
是表示不指向任何對象的對象引用的常量癞季。
this
在次構(gòu)造函數(shù)(二級構(gòu)造函數(shù))中調(diào)用同一個類中的另一個構(gòu)造函數(shù)劫瞳。
class Person(val name: String) {
constructor(name: String, paret: Person) : this(name) {
parent.children.add(this)
}
constructor(name: String, parent: Person, count: Int) : this(name) {
parent.children.add(this)
}
}
typealias
類型別名為現(xiàn)有類型提供替代名稱。如果類型名稱太長绷柒,您可以引入不同的較短的名稱志于,并使用新的名稱。
縮短長泛型類型:
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
可以為功能類型提供不同的別名:
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
as
as
是一個中綴操作符废睦。
用于類型轉(zhuǎn)換
as
是不安全的轉(zhuǎn)換操作符伺绽,如果as
轉(zhuǎn)換失敗,會拋出一個異常嗜湃,這就是不安全的奈应。
val x: String = y as String
上面的代碼表示將y
強轉(zhuǎn)為String
類型,如果y
為null购披,那么將不能轉(zhuǎn)換成String
杖挣,因為String
是不可空的類型,那么就會拋出一個異常今瀑,所以如果y
的類型是可空類型的話程梦,那么強轉(zhuǎn)的類型就必須是可空的
val x: String? = y as String?
用于指定導(dǎo)入包的別名
as
除了用于類型轉(zhuǎn)換之外点把,還有一個作用就是可以指定導(dǎo)入包的別名
import foo.Bar // Bar 可訪問
import bar.Bar as bBar // bBar 代表“bar.Bar”
as?
as?
與as
類似,也是轉(zhuǎn)換操作符屿附,但是與as
不同的是郎逃,as?
是安全的,也就是可空的挺份,可以避免拋出異常褒翰,在轉(zhuǎn)換失敗是會返回null
val x: String? = y as? String
as
后面的類型是個可空的類型,而as?
后面的類型確實非空類型匀泊,但是as?
轉(zhuǎn)換的類型卻是可空的优训,這樣是主要的區(qū)別。
if
和else
在Kotlin中各聘,if
表達式表示返回一個值(true
或false
)揣非,Kotlin中沒有三目運算符。
而else
與Java定義一樣躲因,定義一個if
表達式條件為false
時執(zhí)行的分支早敬。
//傳統(tǒng)用法
var max = a
if (a < b)
max = b
//帶 else
var max: Int
if (a > b)
max = a
else
max = b
//作為表達式
val max = if (a > b) a else b
true
和false
指定布爾類型的"真"值和"假"值。
while
和do
while
是開始一個while
循環(huán)(前置條件的循環(huán))大脉,而do
為開始一個do/while
循環(huán)(后置條件的循環(huán))搞监,do...while
與Java的一樣,有一個區(qū)別是镰矿,語句塊里面的變量在外面是可見的
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y 在這是可見的
for
for
表示開始一個for
循環(huán)
for (item: Int in ints) {
// ...
}
when
Kotlin中的when
就類似與Java的switch
琐驴,但是與switch
不同的是,when
在其它分支都不匹配的時候默認匹配 else
分支秤标,如果沒有把所有可能和分支條件列出來绝淡,那么else
是強制的,這與switch
的default
也有區(qū)別抛杨。
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 默認
print("x is neither 1 nor 2")
}
}
break
break
用于終止循環(huán)的執(zhí)行,使用break
跳轉(zhuǎn)到標(biāo)簽處够委,跳出循環(huán)
loop@ for (i in 1..10) {
for (j in i..10) {
if (j == 5)
break@loop // 跳出循環(huán)
Log.e(Tag, j.toString()) // j 為5的時候跳出了循環(huán),只打印1怖现、2茁帽、3、4
}
}
continue
continue
用于跳到最近的閉合循環(huán)的下一次循環(huán)
loop@ for (i in 1..10) {
for (j in i..10) {
if (j == 5)
continue@loop // 跳出本次循環(huán)屈嗤,進行下一次循環(huán)
Log.e(Tag, j.toString()) // j 為5的時候跳出了循環(huán)潘拨,所有不會打印5
}
}
return
return
默認從最直接包圍它的函數(shù)或者匿名函數(shù)返回。
fun foo() {
ints.forEach {
if (it == 0) return // 跳出forEach
print(it)
}
}
fun
fun
表示聲明一個函數(shù)
fun test() {
}
in
用于指定for
循環(huán)中迭代的對象
for (item in collection) print(item)
用作中綴操作符以檢查一個值屬于一個區(qū)間饶号、一個集合或者其他定義contains
方法的實體铁追。
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
if(a in b){ // a in b等同于b.contains(a)
println("a in b")
}
在when
中使用
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
else -> print("none of the above")
}
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 擁有類型 Double,它是 Number 的子類型
// 因此茫船,我們可以將 x 賦給類型為 Comparable <Double> 的變量
val y: Comparable<Double> = x // OK琅束!
}
!in
!in
表示與in
相反
用作中綴操作符以檢查一個值不屬于一個區(qū)間扭屁、一個集合或者其他定義contains
方法的實體。
if (i !in 1..10) { // 表示i不在1到10區(qū)間
println(i)
}
if(a !in b){ // a !in b等同于!b.contains(a)
println("a !in b")
}
在when
中使用
when (x) {
in 1..10 -> print("x is in the range")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
is
和!is
是否符合給定類型
類似與Java的instanceOf
涩禀,is
操作符或其否定形式!is
來檢查對象是否符合給定類型:
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // 與 !(obj is String) 相同
print("Not a String")
}
else {
print(obj.length)
}
在when
表達式中用于判定是否符合
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
throw
和try
throw
和try
與Java定義一樣料滥,throw
為拋出一個異常,而try
為捕獲異常艾船。
throw MyException("Hi There!")
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally {
// 可選的 finally 塊
}
val
val
表示聲明一個只讀屬性或局部變量
val name: String = ……
var
val
表示聲明一個可變屬性或局部變量
var name: String = ……
軟關(guān)鍵字(Soft Keywords)
以下符號在適用的上下文中充當(dāng)關(guān)鍵字葵腹,而在其他上下文中可用作標(biāo)識符:
import
導(dǎo)入一個包里面的類文件
import foo.Bar // 導(dǎo)入foo包里面的Bar
by
將接口的實現(xiàn)委托給另一個對象
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
將屬性訪問器的實現(xiàn)委托給另一個對象
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
get
聲明屬性的getter
val isEmpty: Boolean
get() = this.size == 0
set
聲明屬性的setter
var stringRepresentation: String
get() = this.toString()
set (value) {
setDataFormString(value) // 格式化字符串,并且將值重新賦值給其他元素
}
dynamic
引用一個Kotlin/JS
代碼中的動態(tài)類型
val dyn: dynamic = ……
catch
與Java一樣,處理異常
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally
與Java一樣屿岂,try
退出時總會執(zhí)行的塊
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally {
// 可選的 finally 塊
}
constructor
聲明一個主構(gòu)造函數(shù)或次構(gòu)造函數(shù)
class Person constructor(firstName: String) {
}
init
主構(gòu)造函數(shù)不能包含任何的代碼践宴。初始化的代碼可以放到以init
關(guān)鍵字作為前綴的初始化塊中:
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
param
、setparam
爷怀、delegate
阻肩、field
、file
霉撵、property
class Example(@field:Ann val foo, // 標(biāo)注 Java 字段
@get:Ann val bar, // 標(biāo)注 Java getter
@param:Ann val quux) // 標(biāo)注 Java 構(gòu)造函數(shù)參數(shù)
使用目標(biāo)(Use-site Targets)支持的有:
file
-
property
使用此目標(biāo)的注解對Java不可見 field
-
get
屬性的getter -
set
屬性的setter -
receiver
擴展函數(shù)或?qū)傩缘慕邮掌鲄?shù) -
param
構(gòu)造函數(shù)參數(shù) -
setparam
屬性的setter的參數(shù) -
delegate
該字段存儲代理屬性的代理實例
receiver
where
whera
用于指定泛型多個類型的上界約束
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable,
T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}
修飾詞關(guān)鍵字(Modifier Keywords)
out
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 這個沒問題磺浙,因為 T 是一個 out-參數(shù)
// ……
}
annotation
annotation
表示聲明一個注解類
annotation class Fancy
companion
companion
表示聲明一個伴生對象
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
const
const
表示將屬性標(biāo)記為編譯期常量,可用于注解當(dāng)中
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
external
external
表示將一個聲明標(biāo)記為不是在 Kotlin 中實現(xiàn)(通過JNI
訪問或者在 JavaScript
中實現(xiàn))
// JNI
external fun foo(x: Int): Double
// JavaScript
external fun alert(message: Any?): Unit
external class Node {
val firstChild: Node
fun append(child: Node): Node
fun removeChild(child: Node): Node
// 等等
}
external val window: Window
inline
聲明一個函數(shù)為內(nèi)聯(lián)函數(shù)
inline fun lock<T>(lock: Lock, body: () -> T): T {
// ……
}
crossinline
crossinline
表示禁止傳遞給內(nèi)聯(lián)函數(shù)的lambda
中的非局部返回
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}
noinline
noinline
表示一個內(nèi)聯(lián)函數(shù)如果只有一些被內(nèi)聯(lián)徒坡,另外的不想內(nèi)聯(lián),可以將函數(shù)禁用內(nèi)聯(lián):
inline fun <T> T.one (inlined: () -> Unit, noinline notInlined: () -> Unit) {
}
如果一個內(nèi)聯(lián)函數(shù)沒有可內(nèi)聯(lián)的函數(shù)參數(shù)并且沒有具體化類型參數(shù)瘤缩,則會產(chǎn)生一個禁告喇完,因為這樣的內(nèi)聯(lián)函數(shù)沒有什么用處。
final
final
為禁止成員覆蓋剥啤。
open class AnotherDerived() : Base() {
final override fun v() {} // v方法不可被重寫
}
open
允許一個類子類化或覆蓋成員锦溪,open
與final
相反,它允許其他類從這個類繼承府怯,默認情況下刻诊,在Kotlin
中所有的類都是final
open class Base(p: Int)
class Derived(p: Int) : Base(p)
data
聲明一個類為數(shù)據(jù)類
data class User(val name: String, val age: Int)
abstract
與Java一樣,abstract
將一個類或成員標(biāo)記為抽象
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
enum
聲明一個枚舉類
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
inner
聲明一個內(nèi)部類牺丙,允許在嵌套類中引用外部類實例
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
sealed
聲明一個密封類(限制子類化的類)
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
lateinit
延遲初始化屬性则涯,允許在構(gòu)造函數(shù)之外初始化非空屬性
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
operator
將一個函數(shù)標(biāo)記為重載一個操作符,也就是操作符重載
override
與Java類型冲簿,override
表示重寫粟判,Derived.v() 函數(shù)上必須加上 override標(biāo)注。如果沒寫峦剔,編譯器將會報錯档礁。
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
private
可見性,將一個聲明標(biāo)記為在當(dāng)前類或文件中可見
protected
可見性吝沫,將一個聲明標(biāo)記為在當(dāng)前類及其子類中可見
internal
可見性呻澜,將一個聲明標(biāo)記為在當(dāng)前模塊中可見
public
可見性递礼,將一個聲明標(biāo)記為在任何地方可見
reified
suspend
將一個函數(shù)或lambda
表達式標(biāo)記為掛起式(可用做協(xié)程)
suspend fun doSomething(foo: Foo): Bar {
……
}
infix
允許以中綴表示法調(diào)用函數(shù)
// 給 Int 定義擴展
infix fun Int.shl(x: Int): Int {
……
}
// 用中綴表示法調(diào)用擴展函數(shù)
1 shl 2
// 等同于這樣
1.shl(2)
tailrec
tailrec
表示將一個函數(shù)標(biāo)記為尾遞歸(允許編譯器將遞歸替換為迭代)
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}
vararg
vararg
表示可變參數(shù)(通常是最后一個參數(shù)):
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // 在這里ts的類型是數(shù)組
result.add(t)
return result
}
使用:
val list = asList(1, 2, 3)
當(dāng)我們調(diào)用vararg
函數(shù),不僅可以接收可以一個接一個傳遞參數(shù)羹幸,例如asList(1, 2, 3)
宰衙,也可以將一個數(shù)組傳遞進去,在數(shù)組變量前面加spread
操作符睹欲,就是*
號:
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4) // 表示(-1, 0, 1, 2, 3, 4)
特殊標(biāo)識符(Special Identifiers)
field
field
為備用字段供炼,Kotlin中的類并不允許使用字段,在自定義getter
和setter
的時候窘疮,可以使用field
來起到局部變量的作用袋哼。
var counter = 0 //初始化值會直接寫入備用字段
get() = field
set(value) {
if (value >= 0)
field = value
}
編譯器會檢查訪問器的代碼,如果使用了備用字段(或者訪問器是默認的實現(xiàn)邏輯),就會自動生成備用字段闸衫,否則就不會涛贯。
// 這種情況并不需要備用字段,所有不會生成備用字段
val isEmpty: Boolean
get() = this.size == 0
注意:
field
標(biāo)識符只允許在屬性的訪問器函數(shù)內(nèi)使用.
it
it
為單個參數(shù)的隱式名稱蔚出,若函數(shù)參數(shù)對應(yīng)的函數(shù)只有一個參數(shù)弟翘,在使用時,可以省略參數(shù)定義(連同->
)骄酗,直接使用it
代替參數(shù):
val doubled = ints.map { it -> it * 2 }
ints.filter { it > 0 } // it表示 '(it: Int) -> Boolean'
這種方式可以寫成LINQ-style代碼:
strings.filter { it.length == 5 }
.sortBy { it }
.map { it.toUpperCase() }
操作符和特殊符號(Operators and Special Symbols)
+
稀余、-
、*
趋翻、/
睛琳、%
數(shù)學(xué)操作符,其中*
還能用于將數(shù)組傳給vararg
參數(shù)
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
=
=
除了作為賦值操作符外踏烙,還用于指定參數(shù)的默認值
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
}
+=
师骗、-=
、*=
讨惩、/=
辟癌、%=
廣義賦值操作符
++
、--
遞增遞減操作符
&&
荐捻、||
黍少、!
邏輯“與”、“或”靴患、“非”操作符仍侥,對應(yīng)的中綴函數(shù)
-
and(bits)
– 位與 -
or(bits)
– 位或 -
xor(bits)
– 位異或 -
inv()
– 位非
==
、!=
相等操作符鸳君,對于非原生類型會翻譯為調(diào)用equals()
===
农渊、!==
引用相等操作符,引用相等由===
(以及其否定形式 !==
)操作判斷。a === b
當(dāng)且僅當(dāng)a
和b
指向同一個對象時求值為true
砸紊。
<
传于、>
、<=
醉顽、>=
比較操作符沼溜,對于非原生類型會翻譯為調(diào)用compareTo()
[
、]
索引訪問操作符游添,會翻譯為調(diào)用get
與set
!!
一個表達式非空
val l = b!!.length
?.
執(zhí)行安全調(diào)用系草,如果接收者非空,就調(diào)用一個方法或訪問一個屬性
b?.length
?:
如果左側(cè)的值為空唆涝,就取右側(cè)的值(elvis
操作符)
val l = b?.length ?: -1
::
創(chuàng)建一個成員引用或者一個類引用
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 輸出 [1, 3]
..
創(chuàng)建一個區(qū)間
val s = 1..10
?
將類型標(biāo)記為可空
val s: String? = null
->
分隔lambda
表達式的參數(shù)與主體
val sum = { x: Int, y: Int -> x + y }
分隔在函數(shù)類型中的參數(shù)類型與返回類型聲明
// less類型是函數(shù)參數(shù)
fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max, it))
max = it
return max
}
分隔 when 表達式分支的條件與代碼體
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
@
引入一個注解
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}
引入或引用一個循環(huán)標(biāo)簽
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
引入或引用一個lambda
表達式標(biāo)簽
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
引用一個來自外部作用域的 this
表達式
class A { // 隱式標(biāo)簽 @A
inner class B { // 隱式標(biāo)簽 @B
fun Int.foo() { // 隱式標(biāo)簽 @foo
val a = this@A // A 的 this
val b = this@B // B 的 this
val c = this // foo() 的接收者找都,一個 Int
val c1 = this@foo // foo() 的接收者,一個 Int
val funLit = lambda@ fun String.() {
val d = this // funLit 的接收者
}
val funLit2 = { s: String ->
// foo() 的接收者廊酣,因為它包含的 lambda 表達式
// 沒有任何接收者
val d1 = this
}
}
}
}
引用一個外部超類
class Bar : Foo() {
override fun f() { /* …… */ }
override val x: Int get() = 0
inner class Baz {
fun g() {
super@Bar.f() // 調(diào)用 Foo 實現(xiàn)的 f()
println(super@Bar.x) // 使用 Foo 實現(xiàn)的 x 的 getter
}
}
}
;
分隔位于同一行的多個語句
map.forEach { _, value ->
println("$value!");println("$value!")
}
$
在字符串模版中引用變量或者表達式
val s = "abc"
val str = "$s.length is ${s.length}" // 求值結(jié)果為 "abc.length is 3"
_
在lambda
表達式中代替未使用的參數(shù)
map.forEach { _, value ->
println("$value!")
}
在解構(gòu)聲明中代替未使用的參數(shù)
val (_, status) = getResult()