本文同步更新于旺仔的個人博客,訪問可能有點慢,多刷新幾次。
Kotlin中有一些常用的關鍵字和標識符,同時還有一些操作符和特殊符號,這些都是和Java有不一樣的地方的,這里將他們介紹一下,方便記憶和回看。
硬關鍵字(Hard Keywords)
Kotlin中的硬關鍵字不能作為標識符
package
與Java一樣,Kotlin的源文件同樣以包聲明開始的。
package foo.bar
fun baz() {}
class Goo {}
// ...
interface
interface
表示聲明一個接口,
interface MyInterface {
fun bar()
fun foo() {
// 可選的方法體
}
}
class
Kotlin的類的聲明與Java一樣,使用class
關鍵字
class Invoice {
}
object
object
為同時聲明一個類及其實例,請看對象表達式。
super
具體內容可看Kotlin學習_類和繼承、接口與實現。
引用一個方法或屬性的超類實現
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
}
在此構造函數中調用超類構造函數
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
null
null
是表示不指向任何對象的對象引用的常量。
this
在次構造函數(二級構造函數)中調用同一個類中的另一個構造函數。
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
類型別名為現有類型提供替代名稱。如果類型名稱太長,您可以引入不同的較短的名稱,并使用新的名稱。
縮短長泛型類型:
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
是一個中綴操作符。
用于類型轉換
as
是不安全的轉換操作符,如果as
轉換失敗,會拋出一個異常,這就是不安全的。
val x: String = y as String
上面的代碼表示將y
強轉為String
類型,如果y
為null,那么將不能轉換成String
,因為String
是不可空的類型,那么就會拋出一個異常,所以如果y
的類型是可空類型的話,那么強轉的類型就必須是可空的
val x: String? = y as String?
用于指定導入包的別名
as
除了用于類型轉換之外,還有一個作用就是可以指定導入包的別名
import foo.Bar // Bar 可訪問
import bar.Bar as bBar // bBar 代表“bar.Bar”
as?
as?
與as
類似,也是轉換操作符,但是與as
不同的是,as?
是安全的,也就是可空的,可以避免拋出異常,在轉換失敗是會返回null
val x: String? = y as? String
as
后面的類型是個可空的類型,而as?
后面的類型確實非空類型,但是as?
轉換的類型卻是可空的,這樣是主要的區別。
if
和else
在Kotlin中,if
表達式表示返回一個值(true
或false
),Kotlin中沒有三目運算符。
而else
與Java定義一樣,定義一個if
表達式條件為false
時執行的分支。
//傳統用法
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
循環(前置條件的循環),而do
為開始一個do/while
循環(后置條件的循環),do...while
與Java的一樣,有一個區別是,語句塊里面的變量在外面是可見的
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y 在這是可見的
for
for
表示開始一個for
循環
for (item: Int in ints) {
// ...
}
when
Kotlin中的when
就類似與Java的switch
,但是與switch
不同的是,when
在其它分支都不匹配的時候默認匹配 else
分支,如果沒有把所有可能和分支條件列出來,那么else
是強制的,這與switch
的default
也有區別。
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 默認
print("x is neither 1 nor 2")
}
}
break
break
用于終止循環的執行,使用break
跳轉到標簽處,跳出循環
loop@ for (i in 1..10) {
for (j in i..10) {
if (j == 5)
break@loop // 跳出循環
Log.e(Tag, j.toString()) // j 為5的時候跳出了循環,只打印1、2、3、4
}
}
continue
continue
用于跳到最近的閉合循環的下一次循環
loop@ for (i in 1..10) {
for (j in i..10) {
if (j == 5)
continue@loop // 跳出本次循環,進行下一次循環
Log.e(Tag, j.toString()) // j 為5的時候跳出了循環,所有不會打印5
}
}
return
return
默認從最直接包圍它的函數或者匿名函數返回。
fun foo() {
ints.forEach {
if (it == 0) return // 跳出forEach
print(it)
}
}
fun
fun
表示聲明一個函數
fun test() {
}
in
用于指定for
循環中迭代的對象
for (item in collection) print(item)
用作中綴操作符以檢查一個值屬于一個區間、一個集合或者其他定義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
相反
用作中綴操作符以檢查一個值不屬于一個區間、一個集合或者其他定義contains
方法的實體。
if (i !in 1..10) { // 表示i不在1到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 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 = ……
軟關鍵字(Soft Keywords)
以下符號在適用的上下文中充當關鍵字,而在其他上下文中可用作標識符:
import
導入一個包里面的類文件
import foo.Bar // 導入foo包里面的Bar
by
將接口的實現委托給另一個對象
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
}
將屬性訪問器的實現委托給另一個對象
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
代碼中的動態類型
val dyn: dynamic = ……
catch
與Java一樣,處理異常
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally
與Java一樣,try
退出時總會執行的塊
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally {
// 可選的 finally 塊
}
constructor
聲明一個主構造函數或次構造函數
class Person constructor(firstName: String) {
}
init
主構造函數不能包含任何的代碼。初始化的代碼可以放到以init
關鍵字作為前綴的初始化塊中:
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
param
、setparam
、delegate
、field
、file
、property
class Example(@field:Ann val foo, // 標注 Java 字段
@get:Ann val bar, // 標注 Java getter
@param:Ann val quux) // 標注 Java 構造函數參數
使用目標(Use-site Targets)支持的有:
file
-
property
使用此目標的注解對Java不可見 field
-
get
屬性的getter -
set
屬性的setter -
receiver
擴展函數或屬性的接收器參數 -
param
構造函數參數 -
setparam
屬性的setter的參數 -
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() }
}
修飾詞關鍵字(Modifier Keywords)
out
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 這個沒問題,因為 T 是一個 out-參數
// ……
}
annotation
annotation
表示聲明一個注解類
annotation class Fancy
companion
companion
表示聲明一個伴生對象
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
const
const
表示將屬性標記為編譯期常量,可用于注解當中
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
external
external
表示將一個聲明標記為不是在 Kotlin 中實現(通過JNI
訪問或者在 JavaScript
中實現)
// 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
聲明一個函數為內聯函數
inline fun lock<T>(lock: Lock, body: () -> T): T {
// ……
}
crossinline
crossinline
表示禁止傳遞給內聯函數的lambda
中的非局部返回
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}
noinline
noinline
表示一個內聯函數如果只有一些被內聯,另外的不想內聯,可以將函數禁用內聯:
inline fun <T> T.one (inlined: () -> Unit, noinline notInlined: () -> Unit) {
}
如果一個內聯函數沒有可內聯的函數參數并且沒有具體化類型參數,則會產生一個禁告,因為這樣的內聯函數沒有什么用處。
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
聲明一個類為數據類
data class User(val name: String, val age: Int)
abstract
與Java一樣,abstract
將一個類或成員標記為抽象
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
聲明一個內部類,允許在嵌套類中引用外部類實例
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
延遲初始化屬性,允許在構造函數之外初始化非空屬性
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
operator
將一個函數標記為重載一個操作符,也就是操作符重載
override
與Java類型,override
表示重寫,Derived.v() 函數上必須加上 override標注。如果沒寫,編譯器將會報錯。
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
private
可見性,將一個聲明標記為在當前類或文件中可見
protected
可見性,將一個聲明標記為在當前類及其子類中可見
internal
可見性,將一個聲明標記為在當前模塊中可見
public
可見性,將一個聲明標記為在任何地方可見
reified
suspend
將一個函數或lambda
表達式標記為掛起式(可用做協程)
suspend fun doSomething(foo: Foo): Bar {
……
}
infix
允許以中綴表示法調用函數
// 給 Int 定義擴展
infix fun Int.shl(x: Int): Int {
……
}
// 用中綴表示法調用擴展函數
1 shl 2
// 等同于這樣
1.shl(2)
tailrec
tailrec
表示將一個函數標記為尾遞歸(允許編譯器將遞歸替換為迭代)
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}
vararg
vararg
表示可變參數(通常是最后一個參數):
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // 在這里ts的類型是數組
result.add(t)
return result
}
使用:
val list = asList(1, 2, 3)
當我們調用vararg
函數,不僅可以接收可以一個接一個傳遞參數,例如asList(1, 2, 3)
,也可以將一個數組傳遞進去,在數組變量前面加spread
操作符,就是*
號:
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4) // 表示(-1, 0, 1, 2, 3, 4)
特殊標識符(Special Identifiers)
field
field
為備用字段,Kotlin中的類并不允許使用字段,在自定義getter
和setter
的時候,可以使用field
來起到局部變量的作用。
var counter = 0 //初始化值會直接寫入備用字段
get() = field
set(value) {
if (value >= 0)
field = value
}
編譯器會檢查訪問器的代碼,如果使用了備用字段(或者訪問器是默認的實現邏輯),就會自動生成備用字段,否則就不會。
// 這種情況并不需要備用字段,所有不會生成備用字段
val isEmpty: Boolean
get() = this.size == 0
注意:
field
標識符只允許在屬性的訪問器函數內使用.
it
it
為單個參數的隱式名稱,若函數參數對應的函數只有一個參數,在使用時,可以省略參數定義(連同->
),直接使用it
代替參數:
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)
+
、-
、*
、/
、%
數學操作符,其中*
還能用于將數組傳給vararg
參數
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)
=
=
除了作為賦值操作符外,還用于指定參數的默認值
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
}
+=
、-=
、*=
、/=
、%=
廣義賦值操作符
++
、--
遞增遞減操作符
&&
、||
、!
邏輯“與”、“或”、“非”操作符,對應的中綴函數
-
and(bits)
– 位與 -
or(bits)
– 位或 -
xor(bits)
– 位異或 -
inv()
– 位非
==
、!=
相等操作符,對于非原生類型會翻譯為調用equals()
===
、!==
引用相等操作符,引用相等由===
(以及其否定形式 !==
)操作判斷。a === b
當且僅當a
和b
指向同一個對象時求值為true
。
<
、>
、<=
、>=
比較操作符,對于非原生類型會翻譯為調用compareTo()
[
、]
索引訪問操作符,會翻譯為調用get
與set
!!
一個表達式非空
val l = b!!.length
?.
執行安全調用,如果接收者非空,就調用一個方法或訪問一個屬性
b?.length
?:
如果左側的值為空,就取右側的值(elvis
操作符)
val l = b?.length ?: -1
::
創建一個成員引用或者一個類引用
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 輸出 [1, 3]
..
創建一個區間
val s = 1..10
?
將類型標記為可空
val s: String? = null
->
分隔lambda
表達式的參數與主體
val sum = { x: Int, y: Int -> x + y }
分隔在函數類型中的參數類型與返回類型聲明
// less類型是函數參數
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)
}
}
引入或引用一個循環標簽
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
引入或引用一個lambda
表達式標簽
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
引用一個來自外部作用域的 this
表達式
class A { // 隱式標簽 @A
inner class B { // 隱式標簽 @B
fun Int.foo() { // 隱式標簽 @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() // 調用 Foo 實現的 f()
println(super@Bar.x) // 使用 Foo 實現的 x 的 getter
}
}
}
;
分隔位于同一行的多個語句
map.forEach { _, value ->
println("$value!");println("$value!")
}
$
在字符串模版中引用變量或者表達式
val s = "abc"
val str = "$s.length is ${s.length}" // 求值結果為 "abc.length is 3"
_
在lambda
表達式中代替未使用的參數
map.forEach { _, value ->
println("$value!")
}
在解構聲明中代替未使用的參數
val (_, status) = getResult()