今天看了篇文章,写点东西做个总结,也算是没有白花时间。
原文地址在这
相对于写惯了Java的人来说,Kotlin算是相当友好的,因为在看完基础语法之后,就完全可以按照写Java的习惯来写Kotlin,并且可以达到一样的效果,这还不算友好吗,这可以算是相当友好。但是,Kotlin还是有着自己一些独特的特点,可以不用,但是了解还是要了解的,相同的就不再啰嗦,这里就把不一样的抽出来说一说。
1. 嵌套类和内部类
在Java里,在类内部声明的类叫做内部类,内部类默认持有外部类的引用,例如,可以在内部类里调用外部类的方法,原因就是持有外部类的引用,自然可以调用其方法,除此之外,通过外部类的实例,才能创建内部类的实例,原因也是在此。要想解除这种引用,那么就要用static修饰,使其变成静态内部类,此时便不再持有外部类的引用,与普通的类无异,唯一的区别就是全类名中含有外部类的名字,依此来定位到自己。
在Kotlin里,大致也是如此,依然是内部类默认持有外部类的引用,但是,声明内部类时需要用inner关键词修饰class,使其变成内部类方可。当没有inner修饰时,和Java中的静态内部类等同,不持有外部类的引用。
平常叫惯了内部类,查了查发现这样是不严谨的,严格来说是叫嵌套类,和静态嵌套类,Static Nested Class。
2. lambda调用外部临时变量
Java7之前,匿名内部类要想使用外部临时变量,必须是final的,到了Java8,引入了lambda,在匿名内部类和lambda中使用外部的临时变量时,可不加final,但是依然不能修改。原因在于,内部类会被编译成一个新的类,其中保留外部类的引用,以及临时变量的副本,所以内部类修改的是变量的副本,这就会造成歧义,所以禁止修改。但这种问题只局限于基本类型变量,如果是引用型变量,就没有这种问题,引用的地址不变,但其指向的内存的数据是可变的。
但是,在Kotlin中,lambda外部的基本类型临时变量也能引用并且可以修改,这就和Java有些不同了。非内联的lambda在编译后会生成一个内部类,这一点同Java,但是对于基本类型也会自动包裹一层,kotlin提供了Ref类,例如IntRef、BooleanRef等,这样基本类型就转成了引用类型,所以也可以在内部类中也可以修改。
3. let/run/apply/also
还是这4个方法,之前写了一篇文章专门介绍,发现分析地再细致,对比的越全面,等到用的时候,还是容易一脸懵:这个和这个的区别是什么来着?现在想了个新角度,如果需要返回自身,就用apply和also,返回lambda的值就用let和run。这样好记了吗,还不好记吗,那就来个更简单的,3个字母的返回lambda,剩下的返回调用者本身。至于lambda内部的参数,不是this就是it,试一下就知道了。
4. 操作符
kotlin里面提供了好大一堆操作符,都是为了方便日常开发使用,要是能记住,用的时候可以少写几行代码,记不住也没关系,就自己写呗,也没几行。这里列举几个出来:
- any:list.any,有一个符合就返回true,any就是任一的意思,与之相反的是none
- all:同上,但相反,必须都符合
- count:统计符合条件的数量
- flod:带初始值,叠加,flodRight,是从最后一项开始;reduce不带角标,reduceRight从最后开始
- forEach:不多说,forEachIndexed,带角标遍历
- max:最大,maxBy,指定对比值,min和minBy同理
- sumBy:求和
- drop:丢掉前n个元素,dropWhile,碰到符合条件后则将剩下的返回,dropLastWhile,从后开始
- filter:过滤,filterNot,取反,filterNotNull,滤掉空
- silce:指定角标
- take:拿前多少个,takeLast,从后面开始拿,takeWhile,返回false时停止拿
- map:映射
- flatMap:展开
- zip:返回由Pair组成的list,unzip将pair list转成
Pair([],[])
5. 委托
在kotlin使用委托在代码上简化了不少,使用by关键字即可
- 接口委托,InterfaceImple虽然实现了接口,但是都是通过调用agent的方法
1
2interface InterF {}
class InterfaceImpl(val agent: InterF) : InterF by agent - 属性委托,将属性委托给一个有getValue和setValue方法的类,val只有get没有set
1
2
3
4
5
6
7class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {}
operator fun setValue(thisRef: Any?, property: KPropergy<*>, value: String) {}
}
var strValue: String by Delegate() - 延迟委托,一般使用lazy方法,也可以自己写一个方法,返回
Lazy<T>
即可,在第一次调用get时,会调用初始化方法1
2
3
4
5
6// LazyThreadSafeMode.SYNCHRONIZED,同步锁
// LazyThreadSafeMode.PUBLICATION,多线程
// LazyThreadSafeMode.NONE,不关心
val lazyValue: String by lazy {
"hello world"
} - 可观察属性,Delegates.observable(initValue, {}),在调用set时会被调用
1
2
3
4
5class Example {
var age: Int by Delegates.observable(-100) { kProperty: KProperty<*>, oldValue: Int, newValue: Int ->
println("kProperty.name: ${kProperty.name} , oldValue: $oldValue , newValue: $newValue")
}
} - 拦截属性值,Delegates.vetoable(initValue, {})
1
2
3
4
5
6class Example {
var age: Int by Delegates.vetoable(-100) { kProperty: KProperty<*>, oldValue: Int, newValue: Int ->
println("kProperty.name: ${kProperty.name} , oldValue: $oldValue , newValue: $newValue")
age <= 0 //返回true 则表示拦截该赋值操作
}
} - 把属性存储在映射中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15fun main() {
val student = Student(
mapOf(
"name" to "leavesCZY",
"age" to 24
)
)
println(student.name)
println(student.age)
}
class Student(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}