kotlin
1、Kotlin中的main函数,不能包在class里,不然无法直接运行
2、基本类型,全是Int这种包装类型,再加上类型推导,正常不需要手动数据类型,但函数的数据类型比较坑
3、数组,声明方式不一样,其它的都一样intArrayOf,区间也是一组连续数字的数组
4、集合框架 不可变List 和 MutableList ,可变与不可变,像数组一样去用它
5、函数是一等公民,这是跟java里 最大的不同,什么高阶函数、lambda都是跟这个特性有关系
6、类和接口,基本是一样的,但里面有的filed的东西,还有一个简略的构造函数的写法,不太一样,接口中可以定义默认的实现
7、扩展方法,可以在文件外,为任意一个类,添加额外的函数方法,这种类似编译注入的方式,好牛逼的样子。
8、空类型安全,使用 ?. !!. ?: 等方式,安全调用类,防止空指针,但在与java混合开发时,需要注意这一点
9、智能类型转换,如果你的上下文中 包含了某一个类型的判断,编译器会自己帮你强转。
10、常量与变量,常量 想要搞出public static final int ,这种常量,你得在最外层使用 const val 去定义 在类与方法里不能定义
11、分支表达式,if else ,when ,try catch,
12、运算符的重载,你可以为任意一个类 定义数学运算符,这些运算符必须是kotlin规定的符号比如(+)就是(plus),然后你用operator fun XX.plus{} 来扩展它
13、中缀表达式,听起来就不好理解,也不知道是谁翻译出来的,使用中缀这两个这么不常用的词,2 to 3 ? = 2,to(3) 实际上它也是一个扩展函数,只是因为它只有一个参数。
所以你可以用另一种形式去使用它,比如infix fun Int.to(s:Int){} ,2 to 3
14、lambda,这个使用与java 8的差不多,不过重要的是,lambda是函数类型,java的lambda是抽象类
15、高阶函数,就是 函数 形参中接收一个函数,或者返回一个函数类型,简单说就是 函数套函数,俄罗斯套娃,套的越多,越高阶,这里要注意函数的引用方式,比如 ::print 把一个print函数的引用传过去。
16、内联函数,inline ,它是编译优化的一个概念,你想哈,如果一个高阶函数,形参中接收一个函数,那大概率使用这高阶函数的中,会传一个匿名函数进去,如果套娃套多了,是会有性能开销的,每一个函数都是一等公民那…
那么inline ,是怎么解决这个问题呢?如果一个函数被 inline 标记,那这个函数中接收到的 形参函数,在编译时 会进行类似“泛型擦除”似的把代码打平,实际上就是把形参传进来的代码,搬到函数内。
多次打平函数,就不再是一个套娃了,就是一串代码,执行效率就会高一些。
但这里也要注意了,如果你传入的函数被打平后,你如果里面有return 那就不是返回了当前函数了,而是终止了 inline 标识的那个函数。这里有两个关键字(crossinline)=禁止local return ; (noline) = 禁用内联,这个用来修饰形参
内联属性,用于backing-field,这玩意会直接优化编译的字节码。inline 也存在嵌套,那inline的嵌套就有一堆限制了,什么public,不能开空间接收inline的函数变量,不能传给其它非inline的函数。
17、let use also apply run,这五个高阶函数 了解下。
18、集合变换与序列,kotlin默认就提供了类似rxjava这种的流式集合操作符,比如map、flatmap、zip等等,有助于提高代码逻辑简洁性。
19、SAM转换,在匿名类函数传递时,注意被SAM转换,导致两次传递的匿名类不是同一个对象,导致问题,以及JAVA中的SAM在Kotlin中的特别之处。
20、类的构造器,使用constructor关键字定义,也可以在类的括号里直接声明变量。init也是初始化代码块。类的括号叫主构造器,其它为副构造器,如果有主构造器存在,则父构造器必须显示调用主构造,防止不清晰的逻辑表达。
通常建议使用主构造 + 默认参数 的方式去构造类,@jvmOverloads,可以标明java的重载,工厂模式,可以使用一个函数去扩展一个类的构造。(工厂函数 如String)
21、internal 模块内可见,一次调用kotlinc的文件中可见。通常一个模块就单独执行一个kotlinc命令,
java中 default的类,可以通过在自己项目中,搞一个一模一样的包名,就可以访问sdk包内的类了,存在缺陷,所以kotlin搞了一个internal概念,但internal概念仅对kotlin代码有约束力,对java的编译器无解,也就是说java代码,可以直接访问kotlin中internal的类。
可以通过@JvmName(“%ab”),就是告诉java的编译器,调这个方法,使用这个%abc的名字,然后这个%在java编译器中是非法的,这样 internal 中的方法就不会直接泄漏了,当然它要反射就无解了,bug一样的存在。
22、view变量 延迟初始化lateinit,可以通过::ss.isInitialized判断是否已经初始化,当然也可以使用可空类型去定义每次去判空,但前面两种都不推荐,可以使用 by lazy关键字
23、delegate 代理(我代替你处理它),分 接口代理 和 属性代理(by lazy) 、 by api(被代理的类),这些代理其作用就是简化代码编写,可以让使用者的代码更具表现力
24、单例object,就是饿汉式单例。@JvmStatic 可以让单例中的成员变为静态成员或方法,@JvmField 可以让一个变量直接可以读写,不使用get\set方法,companion object 伴生对象实际就是在允许在类中去定义静态或其它方法。
25、为什么有@Jvmxxx 这样的东西,因为kotlin野心非常大,它想成为跨平台全栈语言,所以他想要在语言上磨平Java \ JavaScrpt \ Python 这样的差异 就得在语法语义上提取共性,那有一些像各语言特性怎么办?使用特殊标识来提示编译器…这就是@jvm的由来。
26、内部类,这里的概念跟java的一样,有点不一样的是 kotlin 内部类默认就是静态的,如果想要非静态得单独加inner关键字。另外kotlin的匿名内部类更强大,可以单独实现接口。
27、数据类,经常被用来作javabean,被data默认final不能被继承(allopen),且没有无参构造(noarg)。里面有个component的概念,把数据类的参数打成了component1\component2…
28、枚举类,与java的一致,不过枚举的顺序特性在kotlin中,可以被包装为Range区间,可以产生很多有趣的化学反应(语法表达)。enum class xxx()
29、密封类是一种特殊的抽象类,限制子类只能定义在其自身相同的文件中,可以保证子类的衍生数量,枚举的值是有限的,密封类的子类是有限的。 sealed class xxx()
30、内联类是某一个类型的包装,与内联函数一样,inline class xxx(),会在编译时进行优化,把引用的地方,直接搬过去实际的代码,内联类中只能有方法,不能有属性变量,因为无法存储backing field,不能继承与被继承,保证功能的单一。
经典用法,就是替代枚举,安卓中使用枚举性能开销比较大,在java中,可以使用 @IntDef来代替枚举,但在kotlin中失效了。学习一下使用inline class去替代IntDef吧!其它用法自己去探索
31、重要的问题是数据类的序列化与反序列化,在kotlin中的问题,这是大多数安卓开发在转kotlin时的痛点,即使使用noarg之类的插件,也无法解决一些特定问题比如参数默认值,属性代理等,这里要了解一下Kotlinx.serialization
32、泛型,概念与Java一致,在泛型约束时,可以做多约束包括函数类型,比如inline fun maxOf(a:T,b:T)where T :Comparable,T:()->Unit{ return if(a>b)a() else b()} 使用where关键字多约束函数类型
33、泛型,协变与逆变 -> 协变(out T)指的是泛型参数的生产者,通常是返回值类型为泛型 ,逆变(in T)指的是泛型参数的消费者,通常是方法内的参数是泛型类型
interface Book
interface EduBook:Book
class BookStore{
fun getBook():T{
TODO()
}
}
fun main(){
val eduBookStore: BookStore = BookStore()
val bookStore: BookStore = BookStore()
val book:Book = bookStore.getBook()
val book1:Book = eduBookStore.getBook()
val eduBook:EduBook = eduBookStore.getBook()
val eduBook1:EduBook = bookStore.getBook() //这里就报错了
}
open class Waste
open class DryWaste:Waste()
class PhoneDryWaste:DryWaste()
class Dustbin{
fun put(t:T){}
}
fun contravariant(){
val dustbin:Dustbin = Dustbin()
val dryWasteDustbin:Dustbin = Dustbin()
val waste = Waste()
val dryWaste = DryWaste()
val phoneDryWaste = PhoneDryWaste()
dustbin.put(waste)
dustbin.put(dryWaste)
dustbin.put(phoneDryWaste)
dryWasteDustbin.put(dryWaste)
dryWasteDustbin.put(waste)
dryWasteDustbin.put(phoneDryWaste)
}
34、在java中泛型是会被擦除的,所以即使你传了一个T,我也不能正常使用这个T,它只能是一个代号,但kotlin有个 内联特化的概念,可以让T在一定程度上可以使用,inline fun getericMethod(t:T){val ts = Array(3){}}
35、Kotlin中可以使用全部的Java反射机制,Java反射也可以拿到Kt文件的值,但使用Java反射就无法包含kotlin中的新特性,为了解决这个问题,kotlin为反射提供了一个单独的依赖包,可以实现kotlin层的特性反射但这玩意比较大,导包源码加2.5M,编译后体积增加400K,而且第一次使用比较耗时,因为kotlin反射使用了注解处理器,第一次使用时会解析kotlin反射注解,之后使用就不会了。KType\KClass\KProperty\KFunction
36、kotlin的注解,原理与java的一致,不同的是官方加了很多java虚拟机相关的注解,用来适配java平台特性功能。kotlin的注解处理也一样不过依赖时用 kapt 没啥特别的,只是可以用语法糖写出一些奇奇怪怪的东西。
37、协程,就是挂起和恢复,可以控制执行流程的转移,异步逻辑用同步的代码形式写出来。有栈协程,无栈协程。对称协程,非对称协程。async\await
suspend挂起函数,挂起点,