Kotlin学习_关键字与操作符(Keywords and Operators)

Kotlin中有一些常用的关键字和标识符,同时还有一些操作符和特殊符号,这些都是和Java有不一样的地方的,这里将他们介绍一下,方便记忆和回看。

图片来源于网络

硬关键字(Hard Keywords)

Kotlin中的硬关键字不能作为标识符

package

与Java一样,Kotlin的源文件同样以包声明开始的。

1
2
3
4
5
6
7
package foo.bar
fun baz() {}
class Goo {}
// ...

interface

interface表示声明一个接口,

1
2
3
4
5
6
interface MyInterface {
fun bar()
fun foo() {
// 可选的方法体
}
}

class

Kotlin的类的声明与Java一样,使用class关键字

1
2
class Invoice {
}

object

object为同时声明一个类及其实例,请看对象表达式

super

具体内容可看Kotlin学习_类和继承、接口与实现

引用一个方法或属性的超类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
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
}

在此构造函数中调用超类构造函数

1
2
3
4
5
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

null

null是表示不指向任何对象的对象引用的常量。

this

引用当前接收者

次构造函数(二级构造函数)中调用同一个类中的另一个构造函数

1
2
3
4
5
6
7
8
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

类型别名为现有类型提供替代名称。如果类型名称太长,您可以引入不同的较短的名称,并使用新的名称。
缩短长泛型类型:

1
2
3
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>

可以为功能类型提供不同的别名:

1
2
3
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean

as

as是一个中缀操作符。
用于类型转换
as是不安全的转换操作符,如果as转换失败,会抛出一个异常,这就是不安全的。

1
val x: String = y as String

上面的代码表示将y强转为String类型,如果y为null,那么将不能转换成String,因为String是不可空的类型,那么就会抛出一个异常,所以如果y的类型是可空类型的话,那么强转的类型就必须是可空的

1
val x: String? = y as String?

用于指定导入包的别名
as除了用于类型转换之外,还有一个作用就是可以指定导入包的别名

1
2
import foo.Bar // Bar 可访问
import bar.Bar as bBar // bBar 代表“bar.Bar”

as?

as?as类似,也是转换操作符,但是与as不同的是,as?是安全的,也就是可空的,可以避免抛出异常,在转换失败是会返回null

1
val x: String? = y as? String

as后面的类型是个可空的类型,而as?后面的类型确实非空类型,但是as?转换的类型却是可空的,这样是主要的区别。

ifelse

在Kotlin中,if表达式表示返回一个值(truefalse),Kotlin中没有三目运算符。
else与Java定义一样,定义一个if表达式条件为false时执行的分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//传统用法
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

truefalse

指定布尔类型的”真”值和”假”值。

whiledo

while是开始一个while循环(前置条件的循环),而do为开始一个do/while循环(后置条件的循环),do...while 与Java的一样,有一个区别是,语句块里面的变量在外面是可见的

1
2
3
4
5
6
7
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y 在这是可见的

for

for表示开始一个for循环

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

when

Kotlin中的when就类似与Java的switch,但是与switch不同的是,when在其它分支都不匹配的时候默认匹配 else 分支,如果没有把所有可能和分支条件列出来,那么else是强制的,这与switchdefault也有区别。

1
2
3
4
5
6
7
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 默认
print("x is neither 1 nor 2")
}
}

break

break用于终止循环的执行,使用break 跳转到标签处,跳出循环

1
2
3
4
5
6
7
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用于跳到最近的闭合循环的下一次循环

1
2
3
4
5
6
7
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默认从最直接包围它的函数或者匿名函数返回。

1
2
3
4
5
6
fun foo() {
ints.forEach {
if (it == 0) return // 跳出forEach
print(it)
}
}

fun

fun表示声明一个函数

1
2
3
fun test() {
}

in

用于指定for循环中迭代的对象

1
for (item in collection) print(item)

用作中缀操作符以检查一个值属于一个区间、一个集合或者其他定义contains方法的实体。

1
2
3
4
5
6
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中使用

1
2
3
4
5
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
else -> print("none of the above")
}

将一个类型参数标记为逆变

1
2
3
4
5
6
7
8
9
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方法的实体。

1
2
3
4
5
6
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中使用

1
2
3
4
5
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的instanceOfis操作符或其否定形式!is来检查对象是否符合给定类型:

1
2
3
4
5
6
7
8
9
10
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // 与 !(obj is String) 相同
print("Not a String")
}
else {
print(obj.length)
}

when表达式中用于判定是否符合

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

throwtry

throwtry与Java定义一样,throw为抛出一个异常,而try为捕获异常。

1
2
3
4
5
6
7
8
9
10
11
throw MyException("Hi There!")
try {
// 一些代码
}
catch (e: SomeException) {
// 处理程序
}
finally {
// 可选的 finally 块
}

val

val表示声明一个只读属性或局部变量

1
val name: String = ……

var

val表示声明一个可变属性或局部变量

1
var name: String = ……

软关键字(Soft Keywords)

以下符号在适用的上下文中充当关键字,而在其他上下文中可用作标识符:

import

导入一个包里面的类文件

1
import foo.Bar // 导入foo包里面的Bar

by

将接口的实现委托给另一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
}

将属性访问器的实现委托给另一个对象

1
2
3
4
5
6
7
8
9
10
11
12
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

1
2
val isEmpty: Boolean
get() = this.size == 0

用作注解使用处目标

set

声明属性的setter

1
2
3
4
5
var stringRepresentation: String
get() = this.toString()
set (value) {
setDataFormString(value) // 格式化字符串,并且将值重新赋值给其他元素
}

用作注解使用处目标

dynamic

引用一个Kotlin/JS代码中的动态类型

1
val dyn: dynamic = ……

catch

与Java一样,处理异常

1
2
3
4
5
6
try {
// 一些代码
}
catch (e: SomeException) {
// 处理程序
}

finally

与Java一样,try退出时总会执行的块

1
2
3
4
5
6
7
8
9
try {
// 一些代码
}
catch (e: SomeException) {
// 处理程序
}
finally {
// 可选的 finally 块
}

constructor

声明一个主构造函数或次构造函数

1
2
class Person constructor(firstName: String) {
}

init

主构造函数不能包含任何的代码。初始化的代码可以放到以init关键字作为前缀的初始化块中:

1
2
3
4
5
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}

paramsetparamdelegatefieldfileproperty

用作注解使用处目标

1
2
3
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用于指定泛型多个类型的上界约束

1
2
3
4
5
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

将类型参数标记为协变

1
2
3
4
5
6
7
8
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
// ……
}

annotation

annotation表示声明一个注解类

1
annotation class Fancy

companion

companion表示声明一个伴生对象

1
2
3
4
5
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}

const

const表示将属性标记为编译期常量,可用于注解当中

1
2
3
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }

external

external表示将一个声明标记为不是在 Kotlin 中实现(通过JNI访问或者在 JavaScript中实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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

声明一个函数为内联函数

1
2
3
inline fun lock<T>(lock: Lock, body: () -> T): T {
// ……
}

crossinline

crossinline表示禁止传递给内联函数的lambda中的非局部返回

1
2
3
4
5
6
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}

noinline

noinline表示一个内联函数如果只有一些被内联,另外的不想内联,可以将函数禁用内联:

1
2
3
inline fun <T> T.one (inlined: () -> Unit, noinline notInlined: () -> Unit) {
}

如果一个内联函数没有可内联的函数参数并且没有具体化类型参数,则会产生一个禁告,因为这样的内联函数没有什么用处。

warnning

final

final为禁止成员覆盖。

1
2
3
open class AnotherDerived() : Base() {
final override fun v() {} // v方法不可被重写
}

open

允许一个类子类化或覆盖成员,openfinal相反,它允许其他类从这个类继承,默认情况下,在Kotlin中所有的类都是final

1
2
3
open class Base(p: Int)
class Derived(p: Int) : Base(p)

data

声明一个类为数据类

1
data class User(val name: String, val age: Int)

abstract

与Java一样,abstract将一个类或成员标记为抽象

1
2
3
4
5
6
7
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}

enum

声明一个枚举类

1
2
3
4
5
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}

inner

声明一个内部类,允许在嵌套类中引用外部类实例

1
2
3
4
5
6
7
8
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1

sealed

声明一个密封类(限制子类化的类)

1
2
3
4
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

lateinit

延迟初始化属性,允许在构造函数之外初始化非空属性

1
2
3
4
5
6
7
8
9
10
11
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}

operator

将一个函数标记为重载一个操作符,也就是操作符重载

override

与Java类型,override表示重写,Derived.v() 函数上必须加上 override标注。如果没写,编译器将会报错。

1
2
3
4
5
6
7
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}

private

可见性,将一个声明标记为在当前类或文件中可见

protected

可见性,将一个声明标记为在当前类及其子类中可见

internal

可见性,将一个声明标记为在当前模块中可见

public

可见性,将一个声明标记为在任何地方可见

reified

suspend

将一个函数或lambda表达式标记为挂起式(可用做协程)

1
2
3
suspend fun doSomething(foo: Foo): Bar {
……
}

infix

允许以中缀表示法调用函数

1
2
3
4
5
6
7
8
9
10
11
12
// 给 Int 定义扩展
infix fun Int.shl(x: Int): Int {
……
}
// 用中缀表示法调用扩展函数
1 shl 2
// 等同于这样
1.shl(2)

tailrec

tailrec表示将一个函数标记为尾递归(允许编译器将递归替换为迭代)

1
2
3
4
5
6
7
8
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}

vararg

vararg表示可变参数(通常是最后一个参数):

1
2
3
4
5
6
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // 在这里ts的类型是数组
result.add(t)
return result
}

使用:

1
val list = asList(1, 2, 3)

当我们调用vararg函数,不仅可以接收可以一个接一个传递参数,例如asList(1, 2, 3),也可以将一个数组传递进去,在数组变量前面加spread操作符,就是*号:

1
2
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4) // 表示(-1, 0, 1, 2, 3, 4)

特殊标识符(Special Identifiers)

field

field为备用字段,Kotlin中的类并不允许使用字段,在自定义gettersetter的时候,可以使用field来起到局部变量的作用。

1
2
3
4
5
6
var counter = 0 //初始化值会直接写入备用字段
get() = field
set(value) {
if (value >= 0)
field = value
}

编译器会检查访问器的代码,如果使用了备用字段(或者访问器是默认的实现逻辑),就会自动生成备用字段,否则就不会。

1
2
3
// 这种情况并不需要备用字段,所有不会生成备用字段
val isEmpty: Boolean
get() = this.size == 0

注意:field标识符只允许在属性的访问器函数内使用.

it

it为单个参数的隐式名称,若函数参数对应的函数只有一个参数,在使用时,可以省略参数定义(连同->),直接使用it代替参数:

1
2
val doubled = ints.map { it -> it * 2 }
ints.filter { it > 0 } // it表示 '(it: Int) -> Boolean'

这种方式可以写成LINQ-style代码:

1
2
3
strings.filter { it.length == 5 }
.sortBy { it }
.map { it.toUpperCase() }

操作符和特殊符号(Operators and Special Symbols)

+-*/%

数学操作符,其中*还能用于将数组传给vararg参数

1
2
3
4
5
6
7
8
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)

=

=除了作为赋值操作符外,还用于指定参数的默认值

1
2
3
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
}

+=-=*=/=%=

广义赋值操作符

++--

递增递减操作符

&&||!

逻辑“与”、“或”、“非”操作符,对应的中缀函数

  • and(bits) – 位与
  • or(bits) – 位或
  • xor(bits) – 位异或
  • inv() – 位非

==!=

相等操作符,对于非原生类型会翻译为调用equals()

===!==

引用相等操作符,引用相等由===(以及其否定形式 !==)操作判断。a === b 当且仅当ab指向同一个对象时求值为true

<><=>=

比较操作符,对于非原生类型会翻译为调用compareTo()

[]

索引访问操作符,会翻译为调用getset

!!

一个表达式非空

1
val l = b!!.length

?.

执行安全调用,如果接收者非空,就调用一个方法或访问一个属性

1
b?.length

?:

如果左侧的值为空,就取右侧的值(elvis操作符)

1
val l = b?.length ?: -1

::

创建一个成员引用或者一个类引用

1
2
3
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 输出 [1, 3]

..

创建一个区间

1
val s = 1..10

?

将类型标记为可空

1
val s: String? = null

->

分隔lambda表达式的参数与主体

1
val sum = { x: Int, y: Int -> x + y }

分隔在函数类型中的参数类型与返回类型声明

1
2
3
4
5
6
7
8
// 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 表达式分支的条件与代码体

1
2
3
4
5
6
7
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}

@

引入一个注解

1
2
3
4
5
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}

引入或引用一个循环标签

1
2
3
4
5
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}

引入或引用一个lambda表达式标签

1
2
3
4
5
6
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}

引用一个来自外部作用域的 this表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
}
}
}
}

引用一个外部超类

1
2
3
4
5
6
7
8
9
10
11
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
}
}
}

;

分隔位于同一行的多个语句

1
2
3
map.forEach { _, value ->
println("$value!");println("$value!")
}

$

在字符串模版中引用变量或者表达式

1
2
val s = "abc"
val str = "$s.length is ${s.length}" // 求值结果为 "abc.length is 3"

_

lambda表达式中代替未使用的参数

1
2
3
map.forEach { _, value ->
println("$value!")
}

在解构声明中代替未使用的参数

1
val (_, status) = getResult()