类构造器
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 { [email protected]() }
}
}
/***
* 这三个类主要是继承了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()
}