类构造器
class Person(var age: Int(类内全局可见), name: String(init块可见,属性初始化))
init块可以有多个,可以分开写,最终会合并执行
init块可以直接访问构造方法中的参数
定义了主构造器后在类内部再定义的构造器都被称为副构造器
constructor(d: Int): this(d, "")
副构造函数后可调用主构造函数或其他副构造函数,类似java的构造函数重载。
类的可见性
public : 公开(默认)
internal:模块内可见
protected:类内及子类可见
private:类或者文件内可见
延迟初始化
可空类型(使用?修饰)
lateinit:初始化与声明分离,可在后续阶段进行初始化,但是需要注意的是,lateinit会让编译器忽略变量的初始化,不支持int等基本数据类型
lazy:懒加载,初始化与声明内聚,属性首次被访问时执行初始化语句,lazy属性代理,代理了修饰的属性的getter方法。lazy是一个比较推荐的延迟初始化方式,实际上它是一个属性代理。
lazy相当于使用by将属性的get方法代理了,当使用时,才去调用对应的初始化方法。
接口代理
接口代理其实就是可以把一些没必要实现的接口方法隐藏起来,而不用每一个接口都要写一下。其中by关键字右边就是实际代理对象,它是构造函数中的一个属性,by关键字左边是代理类对象实现的接口。
使用前
class ApiWrapper(var api : Api): Api { override fun a() { api.a() } override fun b() { api.b() } override fun c() { println("c is called") api.c() } } interface Api { fun a(); fun b(); fun c(); }
使用后
class ApiWrapper(val api : Api): Api by api { override fun c() { println("c is called") api.c() } } interface Api { fun a(); fun b(); fun c(); }
kotlin单例
只需要在类前面添加object关键字,object定义类等于java的饿汉式单例模式
object类不能定义构造函数,但可以定义init函数
object修饰的类内部方法相当于静态方法,该静态方法是伪静态的,也就是内部会生成一个静态的成员对象,对象的方法调用实际上是调用内部静态成员对象的方法,只有在方法上添加@JvmStatic才会真正的生成静态方法。
@JvmField:使用该注解则不会生成getter/setter方法
内部类
kotlin中类内部的class前面不写修饰符默认就是静态内部类:
class MyClass {}
加inner修饰符就是java中的普通内部类,会持有外部类的对象引用
object类内部的object类默认是静态的,不存在非静态的情况,不能定义成inner
匿名内部类
实际上就是省略了类名直接实现接口
fun main() {
? ? ? ? object : Runnable {
????????}
}
匿名内部类可用继承多个接口
fun main() {
? ? ? ? object : Runnable,Cloneable{
????????}
}
数据类
kotlin中提供一个data关键字,data修饰的类就是一个数据类
data class Person(val id : Long, val name : String, val age : Int)
与java的bean相比,kotlin的data类不能被继承,并且属性要全部写到构造函数当中,没有无参的构造函数。
解构
解构声明允许将一个对象的字段拆解为值或者变量,其内部原理是自动生成了componeN函数。
例如:val p = Person(1, "hello", 20)
val (id, name, age) = p;
使用解构后,可以直接用字段获取值:id,name,age访问数据
data类本身不能被继承,通过查看编译后的代码可以看出,本身被final关键字所修饰。
枚举类
enm class State {
? ? ? ? Idle, Busy
}
枚举类不可继承其他类,但是可以实现接口,可以定义扩展函数
密封类:sealed
密封类是一种特殊抽象类,子类必须定义在自身相同的文件中,个数是有限的
内联类:inline
内联类是对某一个类型的包装,类似于Java装箱类型,编译器会尽可能使用被包装的类型进行优化。
限制:主构造器必须有且仅有一个只读属性,不能定义有backing-field的其他属性,被包装类型不能是范型,不能继承父类也不能被继承,但可以实现接口
Json for Kotlin
moshi
moshi本身使用步骤比gson复杂得多,但同时其功能也更加强大
使用步骤
配置plugins:apply plugins: 'kotlin-kapt'
引入moshi:implementation("com.squareup.moshi:moshi-kotlin:1.11.0")
? ? ? ? ? ? ? ? ? ? kapt("com.squareup.moshi:moshi-kotlin-codegen:1.11.0")
使用时,需要在data class上面添加注解:@JsonClass(generateAdapter = true)
开始序列化与反序列化
@JsonClass(generateAdapter = true) data class moshi(val a : String, val b : Int, val c : Boolean) fun main() { val ms = Moshi.Builder().build() val jsonAdapter = ms.adapter(moshi::class.java) val json = jsonAdapter.toJson(moshi("aaa", 222, false)) println("json:${json}") val mss = jsonAdapter.fromJson("""{"a":"111", "b":333,"c":true}""") println("a:${mss?.a}") println("b:${mss?.b}") println("c:${mss?.c}") }
如果data属性本身给了默认值,则序列化时,会使用该值,否则使用默认值
moshi在使用json反序列化KClass时,如果field是Nullable类型,则可以填入Null,如果是NonNull类型,则在填入Null时会立即抛出异常。
JsonToKotlinClass:该插件用于根据Json字符串生成kotlin的data class
泛型
//泛型 fun <T> maxOf(a: T, b: T): T { return a; } //泛型约束 fun <T : Comparable<T>> maxOf(a: T, b: T): T { return if (a > b) a else b } //多个约束 fun <T> callMax(a : T, b: T) where T : Comparable<T>, T:() -> Unit { if (a > b) a() else b() } //多个泛型参数以及约束 fun <T, R> callMax(a: T, b: T) : R where T : Comparable<T>, T:() ->R, R:Number { return if(a > b) a() else b() } //协变,out标识协变 interface Book interface EduBook : Book class BookStore<out T : Book> { fun getBook() : T { } } fun covariant() { val eduBookStore : BookStore<EduBook> = BookStore<EduBook>() val bookStore : BookStore<Book> = eduBookStore val book : Book = bookStore.getBook() val eduBook : EduBook = eduBookStore.getBook(); } //子类兼容父类 //存在协变点的类的泛型参数必须声明为协变或不变 //当泛型类作为泛型参数类实例的生产者时使用协变 //使用out关键字修饰的泛型就是协变,返回值为协变泛型类型的称为协变点 open class Waste class DryWaste : Waste() class Dustbin<in T : Waste> { fun put(t : T) { } } fun contravariant() { val dustbin : Dustbin<Waste> = Dustbin<Waste>() val dryWasteDustbin : Dustbin<DryWaste> = dustbin; val waste = Waste() val dryWaste = DryWaste() dustbin.put(waste) dustbin.put(dryWaste) dryWasteDustbin.put(dryWaste) } //逆变 //子类兼容父类 //存在逆变点的类的泛型参数必须声明为逆变或不变 //当泛型类作为泛型参数类实例的消费者时用逆变 //使用in关键字修饰的泛型就是逆变,作为函数输入参数的泛型称为逆变点。逆变主要是指输入的参数类型,是消费者。并且消费者的继承关系跟协变是相反的 //星投影 //可用在变量类型声明的位置 //可以描述一个未知的类型 //所替换的类型在协变点返回泛型参数上限类型(也即返回的是类型限制的类型),在逆变点接收泛型参数下限类型(也即限制类型的各种子类) //*不能直接或间接应用在属性或函数上,也即最终确认类型的地方,可以用于未确定对象类型的地方,比如val queryMap: QueryMap<*, *> // fun main() { val queryMap : QueryMap<*, *> = QueryMap<String, Int>(); queryMap.getKey() queryMap.getValue() val f : Function<*, *> = Function<Number, Any>() if (f is Function) { (f as Function<Number, Any>).invoke(1, Any()) } maxOf(1, 3) HashMap<String, List<*>>() val hashMap : HashMap<*, *> = HashMap<String, Int>() } class QueryMap<out K: CharSequence, out V : Any> { fun getKey() : K = TODO() fun getValue() : V = TODO() } class Function<in P1, in P2> { fun invoke(p1 : P1, p2 : P2) = Unit }
内联特化
inline fun <reified T> genericMethod(t : T) {
? ? ? ? val t = T()
? ? ? ? val ts = Array<T>(3){ TODO() }
? ? ? ? val jClass = T::class:java
? ? ? ? val list = ArrayList<T>()
? ? ? ? if(list is List<*>) {
? ? ? ? ? ? ? ? println(list.joinToString())
????????}
}
内联特化在调用的地方会替换到调用处,因此这时类型是确定的了。即已经特化成某个具体类型。通过fun前面的关键字inline和泛型参数T前面的reified参数两个来指定泛型参数在调用处实例化。
class Person(val age: Int, val name: String)
inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java)
fun main() {
? ? ? ? val gson = Gson()
? ? ? ? val person2 : Person = gson.fromJson("""{"age":18,"name":ABC"}""") //类型推导
? ? ? ? val person3 : Person = gson.fromJson<Person>("""{"age":18,"name":"ABC"}""")//泛型参数
}
typealias OnConfirm = () -> Unit typealias OnCancel = () -> Unit private val EmptyFunction = {} open class Notification(val title: String, val content: String) class ConfirmNotification(title: String, content: String, val onConfirm: OnConfirm, val onCancel: OnCancel) : Notification(title, content) interface SelfType<Self> { val self: Self get() = this as Self } open class NotificationBuilder<Self : NotificationBuilder<Self>> : SelfType<Self> { protected var title: String = "" protected var content: String = "" fun title(title: String): Self { this.title = title return self; } fun content(content: String): Self { this.content = content return self } open fun build() = Notification(this.title, this.content) } class ConfirmNotificationBuilder : NotificationBuilder<ConfirmNotificationBuilder>() { private var onConfirm: OnConfirm = EmptyFunction private var onCancel: OnCancel = EmptyFunction fun onConfirm(onConfirm: OnConfirm): ConfirmNotificationBuilder { this.onConfirm = onConfirm; return this } fun onCancel(onCancel: OnCancel): ConfirmNotificationBuilder { this.onCancel = onCancel; return this } override fun build() = ConfirmNotification(title, content, onConfirm, onCancel) } fun main() { ConfirmNotificationBuilder() .title("Hello") .onCancel { println("onCancel") } .onConfirm { println("onConfirmed") } .build() .onConfirm } //如果不定义SelfType类型,则子类在调用ConfirmNotificationBuilder().title("hello")之后,就不能再调用子类的onCancel方法,因为返回的是父类型,但实际运行时这个类型是子类型。 实例:基于泛型实现的Model实例的注入
import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KProperty abstract class AbsModel { init {//这就相当于初始化的时候就将类添加到Models上 Models.run { this@AbsModel.register() } } } /*** * 这三个类主要是继承了AbsModel类, */ class DatabaseModel : AbsModel() { fun query(sql : String) : Int = 0 } class NetworkModel : AbsModel() { fun get(url : String) : String = """{"code":0}""" } class SpModel : AbsModel() { init {//增加一个SpModel2 Models.run { register("SpModel2") println("?????")} } fun hello() = println("Hello World") } //单例 object Models { //保存AbsMode对象,Concurrent表示是带同步锁的 private val modelMap = ConcurrentHashMap<String, AbsModel>() //注入类 fun AbsModel.register(name : String = this.javaClass.simpleName) { modelMap[name] = this; } //创建扩展函数,将返回值转换为实际的对象,这里是利用了扩展函数,将传进来的字符串转换成对象 fun <T:AbsModel> String.get() : T { return modelMap[this] as T } } fun initModel() { DatabaseModel() NetworkModel() SpModel() //创建对象,通过init方法注入到委托 } //单例 object ModelDelegate { operator fun <T:AbsModel> getValue(thisRef : Any, property : KProperty<*> ):T { return Models.run { property.name.capitalize().get() } } } class MainViewModel { //利用代理,将对象的获取通过委托实现 val databaseModel : DatabaseModel by ModelDelegate val networkModel : NetworkModel by ModelDelegate val spModel : SpModel by ModelDelegate val spModel2 : SpModel by ModelDelegate } fun main() { initModel() val mainViewModel = MainViewModel() mainViewModel.databaseModel.query("select * from user").let { println() } mainViewModel.networkModel.get("http://www.baidu.com").let(::println) mainViewModel.spModel.hello() mainViewModel.spModel2.hello() }