Kotlin是一个能够运行在Java虚拟机上的静态类型编程语言, 同时它也能够被编译成JavaScript源码. 它还支持LLVM编译体系.
Koltin的核心开发是JetBrains(IntelliJ IDEA开发公司)驻扎在圣彼得堡(俄罗斯)的一个团队.
虽然Kotlin在语法上与Java有较大区别, 但是它仍然能够和Java代码进行交互并使用Java类库.
从Android Studio 3.0开始Kotlin成为了具有完全支持的Android开发语言.
Kotlin的名字来源于科特林岛(圣彼得堡以西约30公里处), Andrey Breslav宣称团队以岛命名这个语言的灵感来自Java取名于爪哇岛(位于印尼)
目前Kotlin的版本是1.2 (2017年12月时)
val a: Int = 1 //只读
var x: Int = 5 //可读可写
val y: Int? //nullable
fun sum(a: Int, b: Int): Int {
return a + b
}
fun maxOf(a: Int, b: Int) = a + b
Numbers有
以上类型默认在JVM中是以基本类型存储的, 但当使用nullable类型时会转换成包装类
数组在Kotlin里是一个类([]语法通过操作符重载实现)
创建Array是通过一系列arrayOf函数实现的
val array = arrayOf(1, 2, 3)
val x: IntArray = intArrayOf(1, 2, 3)
val s = "Hello, world!\n"
val text = """
for (c in "foo")
print(c)
"""
val s = "abc"
val str = "$s.length is ${s.length}" //字符串模板
if (a < b) max = b
val max = if (a > b) a else b
when (x) {
0 -> print("x == 0")
1, 2 -> print("x == 1 or 2")
in 3..4 -> print("x in 3..4")
parseInt(s) -> print("s encodes x")
else -> print("otherwise")
}
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix") //smart cast
else -> false
}
when替代if else if
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
for (item in collection) print(item)
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y在这里是可见的!
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop //break到loop标签外
}
}
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit //return出lit标签, 也就是lambda表达式
print(it)
}
}
class Foo(a: String, b: Int) {
val aa = a
init { //主构造函数块
print(b)
}
}
class Person(val firstName: String, val lastName: String, var age: Int) { //函数声明和属性声明二合一
// ...
}
class Customer public @Inject constructor(name: String) { ... } //主构造函数的修饰关键字
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) { //如果有主构造函数, 必须申明
parent.children.add(this)
}
- }
val invoice = Invoice() //不需要new
val customer = Customer("Joe Smith")
所有类的父类Any, Any不是Java的Object
class MyView : View {
constructor(ctx: Context) : super(ctx) //如果父类有主构造函数,必须申明
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
open class Base(p: Int) { // open = !final, 默认都是final
open fun v() {}
fun nv() {}
}
class Derived(p: Int) : Base(p) {
override fun v() {}
}
interface Foo { // interface里的函数,属性都是open的
val count: Int
}
class Bar1(override val count: Int) : Foo //在构造函数中重写
class Bar2 : Foo {
- override var count: Int = 0 //用var重写val, 但不能反过来
}
abstract class Derived {
abstract fun f()
}
Kotlin的类能够持有只读的和可读写的属性(Properties), 分别通过val和var关键字定义
class Address {
var name: String = ...
val street: String = ...
}
访问一个类的属性通过.操作符
val result = Address()
result.name = "test"
然而实际上类的属性并不是普通的变量, 通过属性的完整定义语法可以窥探一二
var
[: ] [= ] [
] [
]
一些例子:
var allByDefault: Int? // 错误, 必须要显式的initializer
var initialized = 1 // OK, 推导出type为Int, 使用默认的setter和getter
val simple: Int? // OK, 使用默认的setter和getter, 必须在构造函数内初始化
val inferredType = 1 // OK, 推导出type为Int, 使用默认的getter
val isEmpty: Boolean / /OK, 自定义getter函数
get() = false
var stringRepresentation: String // OK, 自定义的setter和getter
get() = this.toString() // 当前实例的toString
set(value) {
setDataFromString(value)
}
Kotlin的类中不允许持有字段(Fields), 但是Properties实际上仍然是通过Field保存的(叫做backing field). 必要时可以通过field关键字在getter和setter中访问backing field
var counter = 0 // 初始值直接写入backing field
set(value) {
if (value >= 0) field = value // field关键指代property的backing field
}
延迟加载属性可以不设置初值, 在被赋值前访问该属性会抛出异常
lateinit var subject: TestSubject
Kotlin的接口中可以有抽象函数也可以包含实现, 可以具有属性但必须是抽象的或者提供setter,getter
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
get() = "foo"
fun bar()
fun foo() {
// optional body
}
}
无修饰符默认为public
模块为一个项目
Kotlin提供一种在不继承或者使用任何设计模式前提下对类进行拓展的机制.
fun
MutableList .swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' 代表着list
this[index1] = this[index2]
this[index2] = tmp
}
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 像调用成员函数一样调用拓展函数
当在一个类里声明拓展函数时, 会出现多个recevier的问题
class D {
fun bar() { ... }
}
class C {
fun D.foo() {
toString() // calls D.toString() // 拓展函数的receiver优先级比较高
[email protected]() // calls C.toString()
}
}
toString支持在对象为null时调用的拓展函数
fun Any?.toString(): String {
if (this == null) return "null"
//null检查后, this就被自动cast成non-null类型了, 下面的toString就调用对应类的toString
return toString()
}
val
List .lastIndex: Int get() = size - 1
拓展属性只能使用getter和setter,不支持赋初值,因为无法包含backing field
// Java Collections
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list));
// 使用extension
list.swap(list.binarySearch(otherList.max()), list.max());
data class User(val name: String, val age: Int)
数据类会自动生成equals()/hashCode(), toString(), componentN(), copy()函数
带默认值的构造函数
data class User(val name: String = "", val age: Int = 0)
自动生成的copy函数会是下面的样子
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
通过copy方法可以方便的修改一部分值
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
自动生成的componetN()支持的对象解构
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
Sealed Classes机制就像类层面的枚举.
声明了sealed的类是一个抽象类. 它的直接子类必须在同一个文件中定义(子类的子类不受限制).
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr() // 单例模式
sealed类使用when时不需要else
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// 不需要else
}
class Box
(t: T) { var value = t
}
Java泛型中的?通配符很容易让人迷惑
class B
class D extends B
List extends B> //所有能返回B对象的List, List
是其子类型 List super D> //所有能设置D对象的List, List是其子类型
Kotlin提供了更加直观的机制来替代?通配符,它们是Declaration-site variance和Use-site variance
abstract class Bbb
{ abstract fun get(): T //out关键字表明只会返回T类型的数据
abstract fun add(u: U) //in关键字表明只会输入U类型的数据
}
fun demo1(strs: Bbb
) { val objects: Bbb
= strs //由于声明了out关键字, 编译器能够识别出Bbb 是Bbb 的子类型 }
fun demo2(x: Bbb
) { val y: Bbb
= x //由于声明了in关键字, 编译器能够识别出Bbb 是Bbb 的子类型 }
fun copy(from: Array
?, to: Array ?) { }
fun test(){
val f: Array
? = null val t: Array
? = null copy(f, t) //由于out和in关键字的存在, 编译器能够通过编译
}
对于interface Function
fun
singletonList(item: T): List { }
fun
T.basicToString() : String { // extension function }
fun
> sort(list: List ) { //单个继承使用: }
fun
cloneWhenGreater(list: List , threshold: T): List where T : Comparable
, T : Cloneable { //多个继承通过where }
class Outer {
private val bar: Int = 1
class Nested { //=java里加了static
fun foo() = 2
}
inner class Inner { //=java里没有static
fun foo() = bar
}
}
val demo1 = Outer.Nested().foo() // == 2
val demo2 = Outer().Inner().foo() // == 1
enum class ProtocolState(val bit: String) { //能够自定义构造函数
WAITING("123") {
override fun signal() = TALKING
},
TALKING("456") {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState //也能够实现函数
fun bbb() = "bbb"
}
和Java一样,每个枚举值都是该类的一个实例. 有name和ordinal属性
EnumClass.valueOf(value: String): EnumClass //通过名字得到枚举值
EnumClass.values(): Array
//所有枚举值
Kotlin提供了object关键字来定义匿名类
open class A(x: Int) {
public open val y: Int = x
}
interface B {
fun callback()
}
fun test(){
var a = 1
var o = object: A(1), B{ //多继承, 构造函数参数
override val y = 2 //重写属性
val z = 3 //定义数据
override fun callback() { //重写函数
a += add() // 访问a变量
}
fun add() = 1 //定义函数
}
}
匿名类的类型只在作用域内部有效
class C {
private fun foo() = object { //私有函数, 因此返回的类型是匿名类的类型
val x: String = "x"
}
fun publicFoo() = object { //公共函数, 返回Any
val x: String = "x"
}
fun bar() {
val x1 = foo().x // OK
val x2 = publicFoo().x // ERROR
}
}
通过object关键字定义Singleton
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
}
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
}
DataProviderManager.registerDataProvider(...)
DefaultListener.mouseClicked(...)
Kotlin的类没有静态函数和属性, 但是提供了Companion Objects机制实现相关功能
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create() //声明的companion的对象可以直接通过类名引用
class MyClass {
companion object { //companion object可以省略名字, 默认赋名为Companion
}
}
val x = MyClass.Companion
Kotlin对Delegation提供了原生支持
interface Base {
fun print1()
fun print2()
}
class BaseImpl(val x: Int) : Base {
override fun print1() { print(x) }
override fun print2() { print(x+1) }
}
class Derived(b: Base) : Base by b { //通过by关键字, Kotlin会自动生成Base的相关函数并转发到b对象上
override fun print2() { print("a") } //优先本类实现的函数
}
fun main(args: Array
) { val b = BaseImpl(10)
Derived(b).print1() // prints 10
Derived(b).print2() // prints a
}
Kotlin还原生支持对于类属性的委托模式
class Example {
var p: String by Delegate() //由Delegate的一个实例代理p
}
class Delegate { //必须有带operator关键字的getValue和setValue函数, 也可以使用extansion函数
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.'")
}
}
val lazyValue: String by lazy { //lazy函数, 参数是一个函数对象. 能够在属性第一次调用时再为其赋值
println("computed!")
"Hello"
}
fun main(args: Array
) { println(lazyValue)
println(lazyValue)
}
output:
computed!
Hello
Hello
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("
") { //observable函数两个参数, 初始值 和 一个函数对象. 还有一个vetoable函数, 能够决定是否允许此次赋值 prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array
) { val user = User()
user.name = "first"
user.name = "second"
}
outputs:
-> first first -> second
class MutableUser(val map: MutableMap
) { var name: String by map
var age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
user.age = 20
println(user.name) // Prints "John Doe"
println(user.age) // Prints 20
Kotlin标准库也提供了两个委托接口, 方便自定义代理类
interface ReadOnlyProperty
{ operator fun getValue(thisRef: R, property: KProperty<*>): T
}
interface ReadWriteProperty
{ operator fun getValue(thisRef: R, property: KProperty<*>): T
operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
源代码
class C {
var prop: Type by MyDelegate()
}
编译器会生成如下代码:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
class ResourceDelegate
: ReadOnlyProperty { override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader
(id: ResourceID ) { //生成器 operator fun provideDelegate( //必须实现的函数, 参数和getValue一样, 返回一个属性委托对象
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty
{ checkProperty(thisRef, prop.name)
// create delegate
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { ... }
}
class MyUI {
fun
bindResource(id: ResourceID ): ResourceLoader { ... } val image by bindResource(ResourceID.image_id) //一个函数搞定所有属性, 如果没有生成器, 构造Delegate就需要多一个参数指名对应的property是哪个
val text by bindResource(ResourceID.text_id)
}
函数定义
fun read(b: Array
, off: Int = 0, len: Int = b.size): String { //fun关键字, 允许默认值 return "test"
}
函数默认值在重写时不允许更改
open class A {
open fun foo(i: Int = 10) { ... }
}
class B : A() {
override fun foo(i: Int) { ... } //不许再设置默认值
}
调用有默认值的函数时的一些trick
fun foo(bar: Int = 0, baz: Int) { /* ... */ }
foo(baz = 1) // 要使用左边的参数的默认值时, 右边参数必须以命名参数方式传递
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /* ... */ }
foo(1) { println("hello") } // 当最后一个参数是lambda时, 可以把lambda放到()后边, 不用使用命名参数
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
// ...
}
reformat(str, wordSeparator = '_') //可以只提供不使用默认值的参数的值
需要注意顺序参数必须都放在命名参数前面.
fun foo(): Unit { //声明Unit
print("123")
}
fun foo() { //或者省略返回值类型
print("123")
}
fun double(x: Int): Int = x * 2
fun double(x: Int) = x * 2 //返回值类型可以通过表达式推断出时, 可以省略(只有单表达式函数可以)
fun asList(vararg ts: Int): List
{ //vararg关键字 val result = ArrayList
() for (t in ts) // ts是一个数组
result.add(t)
return result
}
val list = asList(1, 2, 3)
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4) //*a作用是把数字a分裂开传入
infix fun Int.shl(x: Int): Int { //infix 关键字 而且 必须是extension函数 而且 只有一个参数 才有效
//...
}
1 shl 2 //中缀形式调用
//等于
1.shl(2)
Kotlin允许直接在文件中定义函数而不需要像java一样创建一个类
Kotlin还支持在函数内部定义函数
fun dfs(graph: Graph) {
val visited = HashSet
() fun dfs(current: Vertex) {
if (!visited.add(current)) return //允许访问外部函数的局部变量
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
高阶函数就是那些以函数为参数或者返回函数的函数
fun
lock(lock: Lock, body: () -> T): T { //body是一个无参返回T的函数 lock.lock()
try {
return body() //调用body
} finally {
lock.unlock()
}
}
类似匿名函数. 总是以{}开始和结束, 参数写在->左边, 执行代码写在->右边, 如果不显式return, 最后一行就是Lambda表达式的返回值
fun
List .map(transform: (T) -> R): List { //...
}
val doubled = ints.map { value -> value * 2 }
val doubled = ints.map { it * 2 } //当lambda表达式只有一个参数时,可以省略参数定义. 使用it作为该参数的名字
map.forEach { _, value -> println("$value!") } //不使用的参数可以用_替代
有时候我们需要显示指明函数的返回类型, 这个时候可以使用匿名函数定义
fun
foo(f: (Int,String)-> T): T{ return f(1, "123")
}
fun test(){
foo(fun(i,s): String = s + "")
foo(fun(i,s): String{
return s+""
})
}
匿名函数和Lambda使用上的一个显著区别就是return, 匿名函数的return退出匿名函数, 普通的lambda不支持无标签的return
匿名函数和Lambda(包括局部函数,object表达式)都能够访问它的闭包(比如局部变量)
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)
函数文字量可以直接使用在其receiver上
fun foo(sum : Int.(other: Int) -> Int){ //Int是receiver
1.sum(2)
}
inline关键字可以表示函数为内联函数
对于高阶函数(参数是函数的),inline关键字同时作用于本函数和参数函数
inline fun
lock(lock: Lock, body: () -> T): T { lock.lock()
try {
body()
} finally {
lock.unlock()
}
}
lock(l) { foo() }会被展开为
l.lock()
try {
foo()
} finally {
l.unlock()
}
内联的参数函数只能在内联函数内部使用,如果需要将它赋值给变量或者作为返回值,需要将其声明为非内联(noinline)
inline foo(noinline a: ()->Int): ()->Int {
return a //如果没有noinline关键字, 编译无法通过
}
return命令不能在lambda表达式内直接使用,退出lambda表达式需要使用@进行标记,
不过对于内联的lambda表达式, 可以使用return退出当前函数
inline fun a(body: () -> Unit) {
body()
}
fun b(body: () -> Unit) {
body()
}
inline fun c(crossinline body: ()->Unit) { //必须设置crossinline关键字, 表明这个函数参数没有在函数体内变换了上下文后再执行的
val r = object:Runnable{
override fun run() {
body()
}
}
r.run()
}
fun foo(){
a {return} //return foo
b {return} //error, 不允许
c {return} //error, 不允许
}
内联函数使用泛型时还支持reified关键字, 能够将泛型类型作为参数使用.
inline fun
Any.be(): T? { if(this is T)
return this
else
return null
}
fun foo(){
"Hello".be
() //null }
没有backfield的类属性也支持inline
class coo {
val a: Int
inline get() = 3 + 4 // get函数inlince
var b: Int
get() = 2
inline set(value) { //set函数inlince
print(value)
}
inline var c: Int //get set都inline
get() = 3
set(value) {
print(value)
}
}
Kotlin支持快速的将类解构为一组独立变量
data class Person(val name:String, val age:Int)
fun foo(){
val person = Person("hxx", 18)
val (name, age) = person
}
Kotlin会把val (name, age) = person编译为
val name = person.component1()
val age = person.component2()
componentN方法是Kotlin对于惯例使用的一个实例. 数据类会自动帮我们实现componentN方法. 如果自己实现要注意为其加上operator关键字
解构声明也能用在for loop里
for ((a, b) in collection) { ... }
还可以用来实现函数的多返回值
fun function(...): Pair
{ return Pair(result, status)
}
val (result, status) = function(...)
可以通过_来填补不使用的位置
val (_, status) = getResult()
也能够用在Lambda表达式中
map.mapValues { entry -> "${entry.value}!" }
等价于
map.mapValues { (key, value) -> "$value!" }
Kotlin不同于许多语言,将集合区分成了可变集合和只读集合. 精确控制集合的可变性有助于减少bug和提供良好的API
Kotlin没有提供专门的创建集合的语法, 而是使用标准库提供的方法来创建集合
val numbers: MutableList
= mutableListOf(1, 2, 3) val readOnlyView: List
= numbers //创建只读视图 val readOnlyCopy: List
= numbers.toList() //创建一份当前list的只读拷贝 val items = listOf(1, 2, 3) //直接创建不可变集合
println(numbers) // prints "[1, 2, 3]"
numbers.add(4)
println(readOnlyView) // prints "[1, 2, 3, 4]"
println(readOnlyCopy) // prints "[1, 2, 3]"
readOnlyView.clear() // error
通过使用to来创建map
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
Ranges表达式是由操作符..以及in或!in组成, 其中的..操作符实际执行的是rangeTo函数.
所有可以比较的类型都能定义range, 不过整型基本类型有着特别优化的实现
if (i in 1..10) { // equivalent of 1 <= i && i <= 10
println(i)
}
所有整型range还有一个额外的功能:可以被遍历
for (i in 1..4) print(i) // prints "1234"
for (i in 4 downTo 1) print(i) // prints "4321"
for (i in 1..4 step 2) print(i) // prints "13"
for (i in 1 until 10) println(i) // i in [1, 10), 10除外
if (obj is String)
print("String")
if (obj !is String)
print("Not a String")
通过is判断后,可以直接将其当作该类型操作
if (x is String)
print(x.length) // x 自动转换为String
if (x !is String) return
print(x.length) // ok
if (x !is String || x.length == 0) return //ok
when (x) { //when也能自动转换
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}
Smart Cast只有在编译器能够确定在次期间变量值不会改变的情况下生效
val x: String? = y as String? //如果y不是String类型而且不是null 则会抛出异常
val x: String? = y as? String //如果y不是String则返回null
泛型的类型检查是在编译时完成的,运行时已经不再保留类型信息,因此不能在运行时进行泛型的类型检查
fun
foo(list: T){ if(list is ArrayList
) //error, 检查类泛型 return
if(list is ArrayList<*>) //ok, 没有检查泛型
return
}
不过对于已经通过静态类型检查的的泛型, 可以对其非泛型部分进行检查并SmartCast成相应类型
fun handleStrings(list: List
) { if (list is ArrayList) { //可以不带<>
//list可以作为ArrayList
使用 }
val alist = list as ArrayList //可以不带<>
}
内联函数通过reified关键字可以对泛型做类型检查(因为内联的展开是编译时完成的)
this表示当前的receiver. 多层嵌套时引用特定receiver需要通过label
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()的receiver, 一个Int
val c1 = this@foo // foo()的receiver, 一个Int
val funLit = lambda@ fun String.() { //显示标签
val d = this // funLit的receiver, 一个String
val d1 = this@lambda // funLit的receiver, 一个String
}
val funLit2 = { s: String ->
// 因为当前lambda没有recevier, 最近的receiver是foo的receiver
val d1 = this // foo()的receiver, 一个Int
}
}
}
}
===是引用比较
==是数值比较
Kotlin允许对预定义的操作符提供自定义实现, 这些操作符有着固定的标示(+,-,*)和优先级.
我们可以通过提供声明了operator并具有固定名字的成员函数或者拓展函数来重载操作符.
一些例子:
Kotlin致力于消失null引用隐患
最常见的null引用错误就是类似Java中NullPointException, 访问一个null引用导致异常
Kotlin的目标是在代码中消除NullPointException
Kotlin将引用区分为可空引用和非空引用
var a: String = "abc"
a = null // 编译错误
var b: String? = "abc" //加上?表示可空
b = null // ok
val l = a.length //非常安全
val l = b.length //有风险,编译错误
可空引用的安全访问
val l = if (b != null) b.length else -1 //基本款
val l1 = b?.length //高级款, b为空时,表达式返回null.可以链式使用,比如:bob?.department?.head?.name
val l = b?.length ?: -1 //尊享款, b为空时,返回-1
通过let方法过滤空值:
val listWithNulls: List
= listOf("A", null) for (item in listWithNulls) {
item?.let { println(it) } // 打印A, 忽略null
}
?:中使用return和throw(return和throw在Kotlin中也是表达式)
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
}
NPE爱好者的福利
val l = b!!.length //b为空, 抛出异常
过滤collection中的null
val nullableList: List
= listOf(1, 2, null, 4) val intList: List
= nullableList.filterNotNull() // (1,2,4)
try {
throw SomeException("Hi There!")
}
catch (e: SomeException) {
// 处理
}
finally {
// 最后处理
}
try-catch也是一个表达式
val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null } //返回try块最后一行或者catch块最后一行. finally块忽略
Kotlin没有throws这样的异常检查机制. 因为异常检查会带来大量无用代码,比如:
// java code
try {
log.append(message)
}
catch (IOException e) {
}
throw表达式的值是Nothing,当一个函数永远无法返回时,可以将其返回值设为Nothing,这样编译器就行进行更多的智能检查
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
}
val s = person.name ?: fail("Name required")
println(s) // 编译器可以确定当前的s一定是被初始化好的
Nothing类型的另一个使用点是, 当一个没有显式声明类型的变量被赋null时,它的类型就会是Nothing?
val x = null // x的类型是Nothing?
val l = listOf(null) // l的类型是List
注解定义
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) //注解对象
@Retention(AnnotationRetention.SOURCE) //存在时间
@Repeatable //是否允许同一个对象上重复使用
@MustBeDocumented //是否时公共api的一部分以及应该被包含在api文档内
annotation class Fancy
使用方法
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}
class Foo @Fancy constructor(dependency: MyDependency) {
// ...
}
class Foo {
var x: MyDependency? = null
@Fancy set
}
val f = @Fancy { Fiber.sleep(10) }
注解可以拥有带参数的构造函数
annotation class Special(val why: String)
@Special("example") class Foo {}
参数类型可以是:
由于Kotlin会自动生成大量代码,就需要对注解进行更细节的控制, 比如对于构造方法
class Example(@field:Ann val foo, // 对Java字段注解Ann
@get:Ann val bar, // 对bar的getter方法注解Ann
@param:Ann val quux) // 对构造方法的quux参数注解Ann
还有其他的注解位置
@file:JvmName("Foo") //对文件注解
package org.jetbrains.demo
class Example {
@set:[Inject VisibleForTesting] //对collaborator的setter方法注解Inject和VisibleForTesting
var collaborator: Collaborator
}
注解位置控制字段有:
如果没有注解位置,Kotlin会基于@Target进行自动选择, 优先级:param > property >field
Kotlin兼容java的注解
val c = MyClass::class //获取Kotlin类引用, 类型时KClass, 如果需要KClass.java是对应的java类引用
val mc = MyClass()
mc::class //通过对象实例也能访问到类引用
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // prints [1, 3], ::isOdd的类型是(Int)->Boolean
构造函数
class Foo
fun function(factory: () -> Foo) {
val x: Foo = factory()
}
function(::Foo)
val x = 1
var y = 1
fun main(args: Array
) { println(::x.get()) // prints "1", ::x的类型是KProperty
println(::x.name) // prints "x"
::y.set(2)
println(y) // prints "2" ::y的类型是KMutableProperty
}
对于类属性
class A(val p: Int)
fun main(args: Array
) { val prop = A::p
println(prop.get(A(1))) // prints "1"
}
对于拓展属性
val String.lastChar: Char
get() = this[length - 1]
fun main(args: Array
) { println(String::lastChar.get("abc")) // prints "c"
}
类似于KClass获取Java类引用, 属性也能获取java相关引用
class A(val p: Int)
fun main(args: Array
) { println(A::p.javaGetter) // prints "public final int A.getP()"
println(A::p.javaField) // prints "private final int A.p"
}
绑定的函数属性引用
val numberRegex = "\\d+".toRegex()
val isNumber = numberRegex::matches
println(isNumber("29")) // prints "true" //isNumber知道numberRegex的数据
绑定和不绑定的区别
val isNumber: (CharSequence) -> Boolean = numberRegex::matches
val matches: (Regex, CharSequence) -> Boolean = Regex::matches
typealias NodeSet = Set
typealias FileTable
= MutableMap >
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate
= (T) -> Boolean
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner