Kotlin学习之05

类构造器

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

新一代Json解析库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()
}
相关推荐
西岸行者9 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意9 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码10 天前
嵌入式学习路线
学习
毛小茛10 天前
计算机系统概论——校验码
学习
babe小鑫10 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms10 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下10 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。10 天前
2026.2.25监控学习
学习
im_AMBER10 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J10 天前
从“Hello World“ 开始 C++
c语言·c++·学习