1. 函数继承与实现、复写等
- 父类需要
open
才可以被继承(kotlin
默认为final
) - 父类的方式、属性需要
open
才可以被覆写 - 接口、接口方法、抽象类默认为
open
- 覆写父类(接口)成员需要
override
关键字 - 注意继承类时,实际上是调用了父类的构造方法
- 类只能单继承,接口可以多实现
2. 接口代理(by
)
接口方法实现交给代理类实现
3. 接口方法冲突(实现多接口)
- 接口方法可以有默认实现
- 签名一致(方法名、参数等一致)且返回值相同的冲突
- 实现类必须覆写冲突方法
- super<[接口名]>.方法名(参数列表)
4.类及其成员的可见性private protect interanl public
protected
:子类可见 internal
:模块内可见
5. Object
关键字
- 只有一个实例的类
- 不能自定义构造函数
- 可以实现接口、继承父类
- 本质上就是单例模式最基本的实现
6. companion object
伴生对象、静态变量成员还有 kotlin
包级成员
kotlin
允许不在类下面写成员变量跟方法,称为包级别对象/函数- 每个类可以对应有一个伴生对象
companion object
- 伴生对象
companion object
与java
的static
静态方法/静态成员的效果类似相同。 kotlin
中的静态成员考虑用包级函数、变量代替JvmField
、JvmStatic
、@file:JvmName(自己自定义的类名)
在Koltin
与java
中调用
可以看到上面
java
调用kotlin
的代码并不像我们java
调用静态对象
一样(中间多了层Companion
)。上面的
java
调用Kotlin
可以在伴生对象中增加对应的注解: 函数上加@JvmStatic
, 变量上加@JvmField
实现类似调用
java
静态成员/方法
具体的大家可以转一下
kotlin
转java
看一下加了注解跟没有加注解的区别,这里因为篇幅原因我就不加代码了。
这里补充一点: java
怎么调用kotlin
的包级别对象呢???
答案:可以使用@file:JvmName(自己自定义的类名)
java
调用:
7. 方法重载跟默认参数
Overloads
方法重载- 名称相同、参数不同的方法
Jvm
函数签名的概念:函数名、参数列表 (不包含返回值!),也就是当两个方法的方法名跟参数一致的时候,这个函数Jvm
视为一个函数。kotlin
中可以为函数参数设置默认值,来实现重载。- 函数调用产生混淆得时候就使用具名函数
java
怎么调用kotlin
的默认参数函数?? 答案:使用@JvmOverloads
注解
java
方法重载造成得bug
,例如List
当List里面传入得是int类型得数据,那么你
remove()
传入一个4
那么它是删除第四位数据还是删除4
这个对象呢?这里它是默认调用了remove(int)
,而remove(Object)
却不会被调用到。
8. 扩展成员
为现有类添加方法、属性
- 扩展方法格式
fun X.y():Z{...}
(fun 被扩展类.自定义扩展方法名():返回类型{方法体 }
) - 扩展属性格式
var X.m
(var 被扩展类.自定义扩展属性名
) 注意扩展属性不能初始化,类似接口属性(X
为被扩展类) java
调用扩展成员类似于调用静态方法
kotlin调用
java调用
9. 属性代理
val delega1 by lazy { }
by
解析
可以看到by
实际上是一个操作符,这里是Lazy
扩展getValue
方法。
lazy
原理解析
剖析一下
lazy
关键代码1:将
lazy(initializer: () -> T)
后面的代码块传入到SynchronizedLazyImpl(initializer)
关键代码2: 进入
SynchronizedLazyImpl
将传入的代码块赋值于initializer
关键代码3:
_value
最开始为未初始化。关键代码4:如果
_value
不为空则直接返回(不执行我们传入的lazy
后面代码块内的方法)关键代码5:如果
_value
为空,做了一下同步的判断如果不为空则直接返回(不执行我们传入的lazy
的方法),否则执行我们的传入的代码块initializer
并拿返回值初始化_value
每次我们调用的时候被
lazy
代理的对象的时候,实际上是调用了里面的value
的get()
,当第一次调用时就会跑我们val delega1 by lazy { 代码块}
代码块中的代码,实例完里面的SynchronizedLazyImpl#_value
后,第二次开始就是直接返回对象。保持只有一个对象实例存在。
- 上面的是
val
属性的代理,lazy
能代理var
的属性吗?
答案:不行。里面并没有实现setValue
的方法。 - 自定义实现代理 下面我们举一下例子 自己来实现一个代理吧。
上面我们实现一个代理类,并实现了
setValue
跟getValue
两个方法。这样就可以代理var
类型的成员变量。 可以看到当我们调用的时候实际上是调用了代理类的setValue
跟getValue
两个方法。
10. 数据类data
&&特殊写法&&解决Javabean
的问题
- 默认实现了
copy、toString
,还有componentN
等方法 - 直接替代
javaBean
存在问题(可能出现无法初始化,构造方法签名不对等问题),因为javaBean
类不是final
类型可被继承,且有一个无参数构造函数。
定义一个data
数据类
看一下转换成java
的实现
- 可以看到
TestKotlin.java
是final
类型的,并且构造函数没有无参的。- 里面实现的
component1
,component2
实际上就是成员变量。- 默认实现了
toString
、hashCode
、equals
等方法。
我们来调用一下 我们写的TestKotlin
数据类
val (name,age)=t
这种特殊写法就是data
类型的对象拥有的。
我们在迭代Array
数组的时候也有这种写法:
最终可以看到
IndexedValue
也是一样的data
类型。
- 处理替代
javaBean
存在问题可以使用,allOpen
跟noArg
插件,前者帮我们去掉类前的final
后者生成无参构造函数
使用方法:
- 在项目(
Project
)下的build.gradle
2.在对应的模块(module
)下的build.gradle
- 新建对应的包名跟文件
...\annotatios\PoKo.kt
- 在
data
类的类名上面添加注解
- 经过上面的注解操作,再
rebuild
一下 再转换成java
代码
可以看到这里已经帮我们生成了无参构造函数跟去掉final
修饰关键字。
注意:因为注解是在编译期的时候生效的,也就是说我们在写代码的时候还是调用不了无参构造函数,但是可以通过反射获取到无参构造函数。
11. 内部类&&匿名内部类
kotlin
默认是静态内部类,非静态内部类需要使用inner
关键字
当内部类需要外部类的状态时,则可以使用非静态内部类,因为非静态内部类默认持有外部类的引用。如果不需要持有外部类的引用,则使用静态内部类。
@Outter
的使用,当非静态内部类跟外部类有成员名字相同时,获取外部类的成员可以用this@外部类名.成员变量
- 匿名内部类(
object
)的使用
不同于java ,Ktolin
的object:
还支持同时实现单继承多实现
12. 枚举类
- 实例有限(可数)的类,一个枚举成员对应一个本类的实例,因为成员是你写上去固定的数目,所以对应的实例也是固定的。
- 如果在
kotlin
的enum
中要定义方法,记得要用;
将成员跟方法隔开
13. 密封类sealed class
子类有限(可数)的类:子类只能在同一个文件中存在,所以其他的文件继承不了它,所以子类有限(可数)。
- kotlin版本<v1.1,子类必须定义为密封类的内部类
- koltin版本>=v1.1,子类只需要于密封类在同一个文件中
- 用于保护类不被外部继承使用,其也是final。