【学习】Kotlin 复习笔记
基础
- ==和equal()相同,===比较内存地址
- 顶级成员(函数&属性)的原理:
Kotlin顶级成员的本质就是Java静态成员,编译后悔自动生成文件名Kt
的类,也可以使用@Jvm:fileName
注解修改自动生成的类名。 - 默认参数的原理:
Kotlin默认参数的本质是将默认值固化
到调用位置,所以在Java中无法直接调用带默认参数的函数,
需要在Kotlin函数上增加@JvmOverloads
注解,指示编译器生成重载方法。 - 解构声明的原理:
Kotlin解构声明可以把一个对象的属性结构为一组变量,所以解构声明的本质就是局部变量。
val (name,price)=Book("Kotlin入门",66.6f)
println(name)
println(price)
------------------------------------------
Kotlin类需要声明`operator fun componentN()`方法来实现解构功能。
否则是不具备解构声明的功能的,例如:
class Book(var name:String,var price:Float){
operator fun component1():String{//解构第一个变量
return name
}
operator fun component2():Float{//解构第二个变量
return price
}
}
- 扩展函数的原理:
扩展函数的语义是在不修改类/不继承类的情况下,向一个类添加新函数或新属性。本质是静态函数,
静态函数的第一个参数是接收者类型,调用扩展时不会创建适配对象或者任何运行时的额外消耗。
在Java中,我们只需要像调用普通静态方法那样调用扩展即可。 - 委托机制的原理:
Kotlin委托的语法关键字是by,其本质上是面向编译器的语法糖,三种委托(类委托,对象委托,局部变量委托)在编译时都会
转化为“无糖语法”。
例如类委托:编译器会实现基础接口的所有方法,并直接委托给基础对象来处理。
例如对象委托和局部变量委托:在编译时会生成辅助属性(prop$degelate),
而属性/变量的getter()和setter()方法只是简单的委托给辅助属性的getValue()和setValue()处理。 - 中缀函数:
声明infix关键字的函数是 中缀函数,可以使用中缀表示法调用(忽略点和括号)
中缀函数的要求:
- 1. 成员函数活扩展函数
- 2. 函数只有一个参数
- 3. 不能使用可变参数或默认参数
举例:
infix fun String.吃(fruit:String):String{
return "${this}吃${fruit}"
}
调用:“小名”吃 "苹果"
类型系统
- 数值类型:
Kotlin将基本数据类型和引用型统一为:Byte、Short、Int、Long、Float、Double、Char和Boolean。
需要注意的是,类型的统一并不意味着Kotlin所有的数值类型都是引用类型,大多数情况下,它们在编译后会变成基本数据类型,
类型参数会被编译为引用类型。
val b:Byte =1 // OK,字面值是静态检测的
val i:Int =b //错误
val i:Int = b.toInt()//ok
- 只读集合 和 可变集合
只读集合只可读,而可变集合可以增删改查(例如List只读,MutableList可变)
需要注意,只读集合引用指向的集合不一定是不可变的,因为你使用的变量可能是众多指向同一个集合的其中一个。 - Array 和 IntArray 的区别:
Array<Int>相当于引用类型数组Integer[],
IntArray相当于数值类型数组int[]
面向对象
- 类修饰符
Kotlin类/方法默认是final的,如果想让继承类/重写方法,需要在基类/基方法添加open修饰符
final: 不允许继承或重写
open:允许继承或重写
abstract:抽象类/抽象方法
- 访问修饰符
Java默认的访问修饰符是protected,
Kotlin默认的修饰符是public
public:所以地方可见
internal:模块中可见,一个模块就是一组编译的Kotlin文件
protected:子类中可见(与Java不同,相同包不可见)
private:类中可见
-
内部类
Kotlin:默认为静态内部类,如果想访问类中的成员方法和属性,需要添加inner关键字称为非静态内部类;
Java:默认为非静态内部类。 -
Object 与 companion Object 的区别
object有两层语义:静态匿名内部类+单例对象
companion object是伴生对象,一个类只能有一个,代表了类的静态成员(函数/属性) -
object单例的原理
lambda表达式
-
lambda表达式本质上是[可以作为值传递的代码块],在老版本Java中,传递代码块需要使用匿名内部类实现,而使用lambda表达式甚至连函数声明都不需要,
可以直接传递代码块作为函数值。 -
当lambda表达式只有一个参数,可以用it关键字来引用唯一的实参
-
lambda表达式的种类
1.普通Lambda表达式:例如()->R
2.带接收者对象的lambda表达式:例如T.()->R -
lambda表达式访问局部变量的原理:
在Java中,匿名内部类访问的局部变量必须是final修饰的,否则需要使用数组或对象座一层包装。
在Kotlin中,Lambda表达式可以直接访问非final的局部变量,其原理是提供了一层包装类,修改局部变量本质上是修改包装类中的值。
class Ref<T>(var value:T)
-
内联函数的原理:
lambda表达式编译后会变成匿名内部类,至少会生成一个中间对象,当lambda表达式被经常调用时,会增大运行开销,使用内联函数可以减少中间对象的开销,
因为调用内联函数不会真正调用函数,而是把函数实现固化到函数调用的位置,需要注意:如果函数体太大就不适合使用内联函数了,因为会大幅度增加字节码大小。 -
实化类型参数 reified:
因为泛型擦除的影响,运行期间不清楚类型实参的实际类型,Kotlin中使用带实化类型参数的内联函数,可以突破这种限制,
实化类型参数在插入到调用位置时会使用类型实参的确切类型代替,因此可以确定实参类型。
在这个函数里,我们传入了一个List,企图从中过滤出T类型的元素
Java:
<T>List<T>filter(List list){
List<T> result =new ArrayList<>();
for(Object e:list){
if(e instanceof T){//compiler error
result.add(e);
}
return result;
}
}
---------------------------------
Kotlin:
fun <T> filter(list:List<*>):List<T>{
val result =ArrayList<T>()
for(e in list){
if(e is T){//cannot check for instance of erased type:T
result.add(e)
}
}
return result
}
调用:
val list =listOf("",1,false)
val strList = filter<String>(list)
-----------------------------------------
内联后:
val result =ArrayList<String>()
for(e in list){
if(e is String){
result.add(e)
}
}
协程
- 协程的原理:使用同步代码编写异步程序
本文来自彭旭锐
查看原文