- 静态类型
- 类型推定
Kotlin 哲学,务实、简洁、安全、与 java 互操。
没有 static
关键字
运算符
*[...]
spread operator
变量
val
,value,常量var
,variable,变量
调用构造方法可以不用 new
关键字。
字符串
字符串模板,"Hello $name You age is ${a+b}"
使用三双引号,必要避免正则表达式转义:"""regex"""
when
整合类型检查与转换
if (e is Num) {
// e 自动当成 Num
//val n = e as Num
}
range 语法糖
for(i in 1..100) // [1,...,100]
for(i in 100 downTo 1 step 2) [100,98,...,2]
for(i in 1 until 100) [1,...,99]
fun
参数支持默认参数,和指定参数名
monkey patch: receiverClass.newFun()
为 receiverClass
增加一个实例方法
vararg
声明一个参数是可变长度的infix
声明一个函数,可以使用 ruby 那样的函数调用方式,方法名称立即放置在目标对象名称和参数之间,没有额外的分隔符。1 to 2
局部函数
fun a(){
fun b(){}
}
Returns and jumps
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
// lambda 内的 return 默认是返回外部函数
if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with explicit label")
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" done with implicit label")
}
}
如果是带有返回值的return@a 1
:”return 1
at label @a
”
集合
- Kotlin没有定义自己的集合类,而是通过扩展函数和属性使用更丰富的API来增强Java集合类。
类
复写方法需要用修饰符 override
super 使用尖括号,来指定方法所属的父类 super<Father>.name()
kotlin 的继承的看法来自 Effective Java:
design and document for inheritance or else prohibit it.
open
关键字修饰的类和方法才可以被继承和重写。
override
修饰的方法,同意继承父类方法可以被继承,需要 `final** 关键字修饰
访问控制修饰符
与 java 的区别有:
- 默认是 public
- internal 对应 java 的默认(package)
- protected 不再拥有包范围内的可访问性
- 外部类看不到内部类的私有成员,内部类默认不持有外部类的引用,除非用
inner
关键字修饰。使用this@Outer
,访问外部类
sealed
sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}
可以限制子类,Expr 只能被 Num 和 Sum 继承。
构造函数
直接在 class
关键字后定义构造函数
class User(val nickname: String)
这称为主构造函数(primary constructor),既定义了构造函数的参数,同时参数也被定义为类的属性。等同于如下代码:
class User constructor(_nickname: String) {
val nickname: String
init {
nickname = _nickname
}
}
主构造函数不能有函数体,所以需要 init block.
constructor
keyword begins the declaration of a primary or secondary constructor. The init
keyword introduces an initializer block.
kotlin 通过 constructor
关键字定义构造函数。跟在类名后定义的构造函数称为主构造函数,在类内部定义的构造函数称为次构造函数(secondary constructors)。
class MyButton : View {
constructor(ctx: Context)
: super(ctx) {
// ... }
constructor(ctx: Context, attr: AttributeSet)
: super(ctx, attr) {
// ...
}
}
是否调用父类方法,采用了与继承相同的操作符:
,如果是调用本类的其他方法,也是接在 :
后面,使用 this
关键字。
Backing Field
field
关键字,用在 getter 和 setter 的作用域中,表示当前成员变量实际存储字段的访问,避免递归调用。
class User{
var firstName : String
get() = field
set(value) {field = value}
var lastName : String
get() = field
set(value) {field = value}
}
data classes
data class Client(val name: String, val postalCode: Int)
data
修饰的类,kotlin 会针对各个属性,生成 equals
、hashCode
、toString
等方法。
对于不可变的 data 类,还提供一个 copy
方便修改生成新实例。
class delegation
kotlin 可以直接实现装饰器模式,通过 by
关键字。
class DelegatingCollection<T>(
innerList: Collection<T> = ArrayList<T>()
) : Collection<T> by innerList {
object 关键字
object
定义一个类,并产生一个实例。实际是为该类生成一个 `INSTANCE** 常量。
- 用于定义单例,定义的同时便创建实例。没有构造函数,因为没有意义。
- `companion** 伴生对象,定义在类内部,可以访问类的私有变量,等同于类的静态方法。(kotlin 没有了 static 关键字)。可或不匿名。
- 用于定义内部匿名类。不是单例,每次定义代码执行都会创建一个新实例
标准库方法
- run
- let
- apply
- also
- with
另外还有
- repeat
- takeIf
- takeUnless
lambda
- 标准语法:
{x: Int, y: Int -> x + y}
- 省略
{ expr }
- 默认参数名,
{ it }
,与 groovy 相同 - 成员引用(member ref),
Class::Member
,对于非类函数可以这样引用:::fun
,构造函数:::Class
,绑定引用:instance::fun
调用 lambda,
{ expr }()
- infix
run
方法:run { expr }
- 如果方法只有一个 lamdbda 参数,可以将 lambda 放在方法调用后面,如果仅有一参数还可以可省略方法调用的括号:
people.maxBy { it.age }
lambda 通用具有闭包的特点,捕获定义时的上下文变量,只不过比起 java 只能捕获 final
,kotlin 捕获的变量是可以修改的。kotlin 实现原理实际是使用一个 Wrapper 类。
Sequences
java 8 的 Stream
,懒操作
people.asSequence()
.map(Person::name)
.filter { it.startsWith("A") }
.toList()
generateSequence
创建序列
receivers
通过 with
和 apply
方法可以设置 lambda 的 receiver。
with(instance){
this // instance
func // instance.func
}
instance.apply{
// same
}
高阶函数
inline
mark a function with the inline modifier, the compiler won’t generate a function call when this function is used and instead will replace every call to the function with the actual code implementing the function.
内联函数支持非本地返回
noinline
,标记内联函数的 lambda 参数不可内联
crossinlne
,标记内联函数的 lambda 参数不可局部返回
类型系统
nullable
Kotlin 的类型不允许空, Type? = Type or null
?.
,safe-call。s?.toUpperCase()
?:
,猫王运算符(Elvis oerator)。a?:0"
,a
不为空返回a
否则返回0
as?
,safe casts。foo as? Type
,等于foo as Type
或null
!!
,非空检查。sNotNull:String = s!!
,等于s
或抛出 NPE(KotlinNullPointerException
)let
,实例方法传入lambda
,在 lambda 中绑定该实例。foo?.let{ ...it... /*not null*/}
,为空的时候 lambda 不执行lateinit
关键字,允许定义变量未初始化,初始化前使用会抛出异常。private lateinit var myService: MyService
基本类型
Int
一般情况被编译为 java 的原生类型,除非是作为类型参数,会被当做Integer
。相同的还有包括其他 java 原生类型- 可空类型的原生类型也会被当成原型类
Any
,是所有非空类型的超类。Unit
java 中的void
。可用于声明函数无返回值:fun f(): Unit{}
。Nothing
表示没有返回值。
操作符重载
conventions
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
也可以定义为扩展函数
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
操作符重载支持不同类型,但 kotlin 不能主动实现交换律,即便运算是可交换的。
compound assignment operators.
+=
对于 plusAssign
,-=
…
一元运算符
一元运算符也可以重载
相同判断
a==b
等价于 a?.equals(b) ?: (b == null)
===
identity equals。判断是否是相同对象,不可被重载
a[b] index operator
与 ruby 一样
[]
get,正常多参数 x[a,b] 等价于 x.get(a,b)[]=
set
destructuring declarations.
val (a,b) = p
等价于 val a = p.component1() val b = p.component2()
,对于 data 类来说 1、2 就是成员属性定义的顺序。对于非 data 类,也可以手动声明
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
泛型
未加限制的泛型默认是可为空的,等价于Any?
Reified(具体化)
Reified(具体的) type parameters,
inline fun <reified T> isA(value: Any) = value is T
可以在函数体类型变量进行检查,类型变量不会被擦除。可以进行如下操作。
- In type checks and casts (is, !is, as, as?)
- To use the Kotlin reflection APIs, as we’ll discuss in chapter 10 (::class)
- To get the corresponding java.lang.Class (::class.java)
- As a type argument to call other functions
reified
关键字只能用于内联函数
val items = listOf("one", 2, "three")
println(items.filterIsInstance<String>())
[one, three]
可变性
非空类型是可空类型的子类型,A
是 A?
的子类型
- 协变,A 是 B 的子类型,f(A) 是 f(B) 的子类型(满足里氏替换原则),则称 f() 是协变的。参数类似是协变的,在kotlin 用
out
修饰,表示类只会产出out
类型的对象,而不能消费。 - 逆变,
-
不变
interface Function1<in P, out R> { operator fun invoke(p: P): R } val f = object : Function1<Number, String> { override fun invoke(p: Number): String = p.toString() } //P 是逆变,Int 是 Number 的子类型, //R 是协变,Any 是 String 的父类型 //所以 Function1<Int, Any> 是 Function1<Number, String> 的父类型,满足里氏替换。 var f2 : Function1<Int, Any> = f
declaration-site variance,在类定义的时候使用 interface Function1<in P, out R>
。use-site variance, java 的 ? extends
? super
每次使用类型变量的时候都可以声明,kotlin 可以在返回值声明 out
,参数声明 in
<*>
Star projection
star-projection syntax you can use to indicate that you have no information about a generic argument.
MutableList<*> is a list that contains elements of a specific type, but you don’t know what type it is.
以 Function1 为例 Function1<*, *>
被编译器认为是 Function1<in Nothing,out Any?>
。
如果不依赖特定的参数化类型的话,就可以使用<*>
,可避免使用 use-site variance
fun printFirst(list: List<*>) {
if (list.isNotEmpty()) {
println(list.first())
}
}
DSL
- reciever
- invoke
Coroutines
避免 callback hell
requestToken
createPost
processPost
Future/Promise/Rx
避免学太多 combinator
suspending functions
composition
coroutine buidle
- launch
async / await
async/await 是 C# 家族的设计,
kotlin 用的是 suspend
关键字,
- 避免 await 关键字,
- 不返回 future
- 为模仿序列化的执行而设计 by default
async 在 kotlin 只是一个方法,await 是 Deferred
的方法
coroutines
java interop
generator
- buildSequence
- yield
jvm
continuation passing style(CPS)
- action
- continuation
requestToken //action
createPost // continuation
cps == callbacks
requestToken{ token -> // action
createPost(token,item) //continuation
}
coroutines direst style
Continuation
suspendCoroutine regular function to suspend function
Coroutine context
what thread it resumes on
map of element
dispatcher
job
CoroutineContext.Element
join cancel,通过 isActive 去判断
Communicating Sequential Processes(CSP)
- Channel
- Actors, named coroutine & inbox channel
Receiver | Suspending function | Select clause | Non-suspending version |
---|---|---|---|
Job | join | onJoin | isCompleted |
Deferred | await | onAwait | isCompleted |
SendChannel | send | onSend | offer |
ReceiveChannel | receive | onReceive | poll |
ReceiveChannel | receiveOrNull | onReceiveOrNull | poll |
Mutex | lock | onLock | tryLock |
none | delay | onTimeout | none |