本文同步更新于旺仔的个人博客,访问可能有点慢,多刷新几次。
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()