kotlin优势
简洁(data class
自带get set equals hashCode toString copy
)
安全(区别可空和不可空,避免各类空指针)
与java互操作性
基础特性及用法
显式
Kotlin 通过细致的显式声明和约束,使代码的逻辑和结构都更加清晰,并减少错误。
可变与只读
var 声明可变变量
val 声明制度变量
只读集合List
Set Map
可变集合MutableList
MutableSet MutableMap 类型安全
是静态隐式类型的,提供类型推断-
空引用安全
区分可空与非空
可空安全调用bob?.department?.head?.name
Elvis
操作符:如果?:
左边的表达式不为null
,则返回该表达式;否则返回null
。val l = b?.length ?: ‐1 // 等效于 val l: Int = if (b != null) b.length else ‐1
集合
集合也可以根据是否允许容纳 null
进行区分(List
)。
可以使用 filterNotNull()
从可为空类型的集合中过滤非空类型。
- 修饰符
可见性
peivate
: 类内可见
protected
:private
+子类可见
internal
:同一模块(一起编译的一组kotlin源文件)
public
:可见
open
:用于继承的类要显示的声明open
inner
:内部类,默认内部类不能访问外部类
使用inner
标记的类会携带外部类引用,可以访问外部类成员
- 工程化
数据类解构声明
data class User(val name: String = "", val age: Int = 0)
数据类自动对每个属性生成对应的componentN()
函数,用于解构声明: val jane = User("Jane", 35)
val (name, age) = jane
- 扩展函数
kotlin提供了对类扩展的功能,可以在不继承或使用设计模式(如装饰着)的情况下,为类添加新的功能
如
下面为MutableList
添加了一个swap()
函数:
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 对应列表本身 this[index1] = this[index2]
this[index2] = tmp
}
同时声明以下两个函数回导致冲突:
fun String?.toDecimal(): BigDecimal {...}
fun toDecimal(value: String?): BigDecimal {...}
扩展函数是静态解析的,其调用取决于声明类型而不是运行时的实际类型
open class C class D: C()
fun C.foo() = "c" fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D()) // c
- 操作符重载
// todo
进阶特性及用法
kotlin中函数的地位
kotlin中函数是头等的。可以存储在变量与数据结构中、可以作为参数传递给高阶函数、也可以从高阶函数返回。
高阶函数
高阶函数是将函数用作参数或返回值的函数。
fun lock(lock: Lock, body: () ‐> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
上述代码中,body
就是函数类型,该函数没有输入参数,输出类型为T。 它被 lock
保护,在 try
块中调用,其结果由 lock()
函数返回。 lock()
的调用方法如下。
fun toBeSynchronized() = sharedResource.operation()
val result1 = lock(myLock, ::toBeSynchronized)
val result2 = lock(myLock, { sharedResource.operation() })
函数类型
Kotlin 使用类似 (Int) -> String
的一系列函数类型来处理函数的声明: val onClick: () -> Unit = ……。
这些类型具有与函数签名对应的特殊标示法,即他们的参数和返回值:
参数放在
()
中,参数放在->
左侧,返回值放在->
右侧。参数列表可为空,返回类型不可省略,如:()->Unit
参数类型可以有一个额外的接收者类型,他的表示法如:
A.(B) -> C
。它表示,参数A对象中有一个方法接收一个B类型的参数,并返回C。挂起函数 一种特殊类型的函数,有一个suspend修饰符。如
suspend ()->Unit
函数类型指定为可空需使用圆括号
((Int,Int) -> Int)?
函数类型可以使用圆括号结合
(Int -> (Int -> Int))
箭头表示法是右结合的
(Int -> (Int -> Unit))
等价于(Int -> Int -> Unit)
但不等价于((Int -> Int) -> Unit)
还可以通过类型别名给函数类型起一个别称:
typealias ClickHandler = (Button,ClickEvent) ->Unit
函数类型实例调用
函数类型的值可以通过invoke(...)
调用:f.invoke(x)
或者直接f(x)
。
如果该值具有接收者类型,那么应该将接收者作为第一个参数传递。调用接收者的函数值类型的另一个方式是在其前面加上接收者对象,就好比该值是一个扩展函数:
例
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // 类扩展调用
- Lamda表达式
没有经过声明而直接作为表达式传递的函数。
编写规则:
1.Lamda表达式要被{}
包围
2.参数(参数类型可以省略)在->
左边
3.函数体在->
右边
4.最后一个表达式作为函数的返回值
val sum1: (Int, Int) ‐> Int = { x, y ‐> x + y }
val sum2 = { x: Int, y: Int ‐> x + y }
sum1(1, 2)
sum2(1, 2)
如果函数的最后一个参数为表达式,且你传递的是Lamda表达式,那么kotlin的惯例是把Lamda表达式写在括号外面:
lock(myLock, { sharedResource.operation() })
// 等效于
lock (myLock) {
sharedResource.operation()
}
- 闭包
Lambda表达式和匿名函数可以访问其闭包,也就是外部作用域声明的变量。不同的是,java可以修改从闭包中获取的变量。
-
匿名函数
val isOdd: (Int) -> Boolean = fun(value: Int): Boolean { return value % 2 == 1 } val isOdd = fun(value: Int): Boolean { return value % 2 == 1 } val isOdd = fun(value: Int): Boolean = value % 2 == 1
使用
val values = listOf(1, 2, 3, 4, 5)
val filtered = filter(values, isOdd)
-
可调用引用
1.引用顶层、局部、成员、扩展函数:::isOdd String::toInt
2.引用顶层、成员、扩展属性:List
::size
3.引用构造器:::Regex
例val filtered = filter(values, ::isOdd)
-
实现了函数类型接口的类。使用方法同普通类一样,定义方法如下:
class OddChecker: (Int) -> Boolean { override fun invoke(value: Int): Boolean { return value % 2 == 1 } }
Lambda语法糖
如果Lambda只有一个参数,且编译器可以推断其类型,则可以省略参数,此时该参数会被隐式的声明为it
val isOdd: (Int) -> Boolean = { value -> value % 2 == 1 } val isOdd: (Int) -> Boolean = { it % 2 == 1 }
val isOdd: (Int) -> Boolean = { it % 2 == 1 }
如果省略回影响编译器的类型推断,则不可省略
val isOdd = { value: Int -> value % 2 == 1 }
如果函数只返回一个表达式,则可以省略函数体的大括号(和返回类型),使用 = 连接函数体
fun double(x: Int): Int { return x * 2 }
fun double(x: Int): Int = x * 2
fun double(x: Int) = x * 2
- 纯函数
1.对于同样的输入,返回同样的输出
2.调用函数不会产生副作用(不会修改程序状态)
-
内置函数
let
T
的扩展函数,将block
中的操作应用在T
上,并返回block
的值,在block
中使用it
引用T
的实例
also
T
的扩展函数,将block
中的操作应用在T
上,并返回T
,在block
中使用it
引用T
的实例
with
将block
中的操作应用在T
上,并返回block
的值,不是扩展函数,block
中使用this
引用T
的实例
run
T
的扩展函数,将block
中的操作应用在T
上,并返回block
的值,在block
中使用this
引用T的实例
apply
T
的扩展函数,将block
中的操作应用在T
上,并返回T
,在block
中使用this
引用T
的实例
-
集合的操作
var records: List
= ... val models: List = records // List .map { it.transform() } // List .filter { it.units.isNotEmpty() } // List .map { food -> food.units.map { unit -> food.deepCopy(listOf(unit)) } } //List - > .flatten() // List
val foodMap: Map > = models.groupBy { it.foodCategoryName } -
参数默认值和命名参数
fun reformat(str: String,normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') { ... }
默认参数调用
reformat(str)
指定参数调用
reformat(str, true, true, false, '_')
使用命名参数,提高代码可读性
reformat(str,normalizeCase = true,upperCaseFirstLetter = true,divideByCamelHumps = false,wordSeparator = '_')
- 委托属性
使用如下形式声明委托属性
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.")
}
}
使用
val e = Example()
e.p = "HelloWorld" // HelloWorld has been assigned to 'p' in Example@17f052a3. println(e.p) // Example@17f052a3, thank you for delegating 'p' to me!
-
标准委托 lazy
val lazyValue: String by lazy { println("computed!") "Hello" } fun main(args: Array
) { println("Running in main") println(lazyValue) println(lazyValue) }
输出
Running in main
computed!
Hello
Hello
-
observable
Delegates.observable接收两个参数:初始值和属性变化时的Handlerclass User { var name: String by Delegates.observable("
") { prop, old, new -> println("$old -> $new") } } fun main(args: Array ) { val user = User() user.name = "first" user.name = "second" }
输出
-> first
first -> second
-
map
class User(val map: MutableMap
) { var name: String by map var age: Int by map } val user = User(mapOf( "name" to "John Doe","age" to 25 )) println(user.name) // John Doe println(user.age) // 25 user.name = "Mary Sue" user.age = 26 println(user.map) // {name=Mary Sue, age=26} 内联函数和具体化类型参数
使用高阶函数会创建额外的函数对象并捕获闭包,带来额外开销。通过inline标注为内联函数,可以避免这些开销。
inline
会同时对函数本身和作为函数参数的lambda表达式生效
inline fun lock(lock: Lock, body: () -> T): T { lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
例
foo()
lock(l) { foo() }
bar()
相当于
foo()
l.lock()
try {
foo()
} finally {
l.unlock()
}
bar()
在内联函数中使用 reified
将范型标记为具体化类型参数,在内联函数中,可以像访问参数一样访问具体化类型参数。
inline fun fromJson(json: String): T =
gson.fromJson(json, T::class.java)
val user = fromJson(json)
val user: User = fromJson(json)
- 支持字段
在类或累的主构造器中使用var
或val
声明类的属性,不能再类中声明Filed
,Kotlin会自动在需要的时候为属性添加一个支持字段。
class Person {
var age: Int = 10
get() {
return field
}
set(value) {
field = value
}
}
-
具有支持字段的属性
如果属性使用了至少一个默认的访问器
(get/set)
,或者自定义访问器中使用了filed
标识符访问了支持字段,则会生成对应的支持字段。class Person( val name: String, var age: Int){ val isAdult: Boolean = age >= 18
}
这里的isAdult
相当于java的public final boolean isAdult = age >= 18
val person = Person("John", 17)
println(person.isAdult) \ false p
erson.age++
println(person.isAdult) \ false -
没有支持字段的属性
class Person( val name: String, var age: Int ){ val isAdult: Boolean get() { return age >= 18 } // 等同于get() = age >= 18 }
这里的isAdult
相当于java的
public boolean isAdult() {
return age >= 18;
}
val person = Person("John", 17)
println(person.isAdult) \\ false
person.age++
println(person.isAdult) \\ true