美文网首页Kotlin
kotlin<第二篇>:类与继承

kotlin<第二篇>:类与继承

作者: NoBugException | 来源:发表于2022-08-09 01:25 被阅读0次
    一、类的定义
    class Test {
    }
    
    二、构造函数
    主构造函数:
    
    class Test(param1 : String, param2 : Int) {
    
        init {
            println("param1:$param1")
            println("param2:$param2")
        }
    
    }
    
    还可以添加constructor关键字
    
    class Test constructor(param1 : String, param2 : Int) {
    
        init {
            println("param1:$param1")
            println("param2:$param2")
        }
    
    }
    
    次构造函数:
    
    class Test {
    
        constructor() { }
    
        constructor(param1 : String, param2 : Int) { }
    
    }
    
    如果存在主构造函数,必须继承主构造函数:
    
    class Test (param1 : String, param2 : Int) {
    
        constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {
    
        }
    
    }
    
    初始化块(init)和次构造函数的执行顺序:
    
    我们需要知道,init代码块是主构造函数初始化的地方,当主构造函数被实例化的时候,init代码块必然执行。
    当次构造函数被实例化的时候,init代码块首先被执行,然后才会执行次构造函数的实现。
    
    私有构造函数:
    
        class Test private constructor(param1 : String, param2 : Int) {
    
        private constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {
          
        }
    }
    
    构造函数时可以被私有的,如果被私有,外部将不能访问,也就是说,在外部,不能对该构造方法进行实例化。
    
    private 关键字放在 constructor 前面,默认情况下是 public,public情况下,主构造函数的 constructor 关键字可以被省略。
    
    三、继承
    Kotlin和Java不同,Kotlin任何一个非抽象类默认不可以继承,添加 open 关键字可以让非抽象类继承。
    
    open class Person { 
     ... 
    } 
    
    例如:
    
    class Student(param1: String, param2: Int) : Person(param1, param2) {
    }
    
    open class Person constructor(param1: String, param2: Int){
    }
    
    kotlin中函数的重载(覆盖):
    
    open class Person constructor(param1: String, param2: Int){
    
        fun doSomething(){
            println("Person doSomething")
        }
    }
    
    class Student(param1: String, param2: Int) : Person(param1, param2) {
    }
    
    val person: Person = Student("zhangsan", 13)
    person.doSomething()
    
    打印结果是:
    Person doSomething
    
    在子类Student中可以重载父类的方法:
    
    class Student(param1: String, param2: Int) : Person(param1, param2) {
    
        override fun doSomething(){
            println("Student doSomething")
        }
    
    }
    
    在kotlin中,子类默认是不允许重载父类的方法,如果想要重载,父类的方法必须使用open关键字修饰:
    
    open class Person constructor(param1: String, param2: Int){
    
        open fun doSomething(){
            println("Person doSomething")
        }
    
    }
    
    如此,打印结果是:
    Student doSomething
    
    变量的覆盖:
    
    在父类中,存在一个成员变量 name
    open class Person constructor(param1: String, param2: Int){
    
        val name: String = "zhangsan"
    
    }
    
    在子类中可以访问父类的成员变量:
    
    class Student(param1: String, param2: Int) : Person(param1, param2) {
    
        fun doSomething(){
            println("my name is $name")
        }
    
    }
    
    如果子类中定义一个和父类成员变量一模一样的成员变量,那么父类的成员变量就被子类的成员变量覆盖了:
    
    open class Person constructor(param1: String, param2: Int){
    
        open val name: String = "zhangsan"
    
    }
    
    class Student(param1: String, param2: Int) : Person(param1, param2) {
    
        override val name: String = "lisi"
    
        fun doSomething(){
            println("my name is $name")
        }
    
    }
    
    父类的成员变量使用 open 修饰, 子类的成员变量使用 override 修饰。
    
    
    派生类初始化顺序:总是首先初始化基类,然后才会初始化派生类
    
    抽象类:
    
    abstract class Person {
    
    }
    
    使用 abstract 修饰表示一个抽象类,抽象类不能被实例,只能被继承,而 open 关键字修饰的类既可以继承,也可以被实例化。
    
    四、get 和 set 函数
    在Java中,定义一个bean是常见的,在bean里面存在 set 和 get 函数,
    kotlin中 set 和 get 的语法和ava存在很大的区别:
    
    class Student {
        var name: String
            get() = name
            set(value) {
                this.name = value
            }
    }
    
    在 kotlin 中, set 和 get 函数可以写在成员变量后面。
    如上代码,set 和 get 没有其它特别处理,可以将 set 和 get 省略。
    
    五、延迟初始化
    private lateinit var textView: TextView
    
    当你对一个全局变量使用了lateinit关键字时,请一定要确保它在被任何地方调用之前已经完成了初始化工作,
    否则Kotlin 将无法保证程序的安全性。
    
    判断是否已经初始化
    if (::textView.isInitialized)
    
    六、接口
    接口的定义和声明:
    
    interface MyInterface {
        fun doSomething()
    }
    
    接口的实现:
    
    class Student : MyInterface{
    
        override fun doSomething() {
            TODO("Not yet implemented")
        }
    
    }
    
    kotlin 和 java一样,都支持多个接口实现。
    
    六、可见性修饰符
    public、private、protected和internal
    
    默认修饰符是 public
    
    internal:只对同一模块下可见
    
    七、数据类
    使用 data 关键字,声明该类为数据类
    
    data class Cellphone(val brand: String, val price: Double) 
    
    Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等
    且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。
    
    八、密封类
    // sealed:密封类,同时具有枚举和普通类的特性
    // 密封类及其子类必须声明在同一个 kotlin 文件中
    sealed class Result // 反编译得到抽象类
    class Success(val code: Int) : Result()
    class Exception(val code: Int, val message: String) : Result()
    
    // 在密封类Result中,只定义了两个子类,所以在使用when语句时,不需要else
    fun handleResult(result: Result): String{
        return when(result) {
            is Success -> {
                "success"
            }
            is Exception -> {
                "exception"
            }
        }
    }
    
    九、内部类和嵌套类
    Kotlin 内部类与嵌套类的区别是:
    1、内部类会带有一个外部类的对象的引用,嵌套类则没有
    2、内部类需要使用 inner class 定义,而嵌套类则使用 class 定义
    
    Kotlin 内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
    
    Kotlin 内部类使用 this@[外部类名] 持有外部类对象的引用。
    
    十、委托类
    类委托:于将一个类的具体实现委托给另一个类去完成
    
    interface Base {
        fun fixbug()
    }
    
    class ZhangsanDo : Base {
        override fun fixbug() {
            println("zhangsan fixed a bug")
        }
    }
    
    class Lisi : Base {
        override fun fixbug() {
            println("lisi fixed a bug")
        }
    }
    
    假设有一个bug, 张三可以解决bug,李四也可以解决bug,如果领导将bug交给张三解决:
    
    val base1 : Base = ZhangsanDo()
    base1.fixbug()
    
    如果领导将bug交给李四给解决:
    
    val base2 : Base = Lisi()
    base2.fixbug()
    
    但是,存在一种情况,就是李四无法解决这个bug,所以他想委托为张三去解决:
    
    interface Base {
        fun fixbug()
    }
    
    class ZhangsanDo : Base {
        override fun fixbug() {
            println("zhangsan fixed a bug")
        }
    }
    
    class Lisi : Base by ZhangsanDo() // 委托给张三
    
    如果领导将bug交给张三,张三直接解决:
    
    val base1 : Base = ZhangsanDo()
    base1.fixbug()
    
    如果领导将bug交给李四,李四将bug委托给张三,本质上,bug是由张三解决的:
    
    val base2 : Base = Lisi()
    base2.fixbug()
    
    十一、委托属性
    class Example {
        var p: String by Delegate() // 将p属性的具体实现委托给了Delegate类去完成
    }
    class Delegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "$thisRef, thank you for delegating '${property.name}' to me!"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("$value has been assigned to '${property.name}' in $thisRef.")
        }
    }
    
    当 p 使用时就会自动执行 Delegate 的 getValue 方法,当 p 被赋值时,就会自动执行 Delegate 的 setValue方法。
    
    val e = Example()
    println(e.p)
    
    打印结果是:
    Example@234bef66, thank you for delegating 'p' to me!
    
    val e = Example()
    e.p = "NEW"
    
    打印结果是:
    NEW has been assigned to 'p' in Example@234bef66.
    
    我们可以发现,当对象第一次被调用时:执行getValue函数,当对象第一次赋值时执行setValue方法。
    
    
    下面使用委托属性,实现懒加载:
    
    class Later<T>(val block : () -> T) {
    
        var value : Any? = null
    
        /**
         * 对象第一次被调用时执行
         */
        operator fun getValue(any: Any?, property: KProperty<*>): T {
            println("getValue")
            if (value == null) {
                value = block()
            }
            return value as T
        }
    
        /**
         * 被复制或对象属性发生变化时执行
         */
        operator fun setValue(any1: Any?, property: KProperty<*>, any2: Any) {
            println("setValue")
        }
    
    }
    
    var p by Later<String> {
        println("p 执行了")
        "A"
    }
    p.toString()
    
    kotlin已经为我们实现了懒加载机制:by lazy {}
    
    private val uriMatcher by lazy {
        val matcher = UriMatcher(UriMatcher.NO_MATCH)
        matcher.addURI(authority, "book", bookDir)
        matcher.addURI(authority, "book/#", bookItem)
        matcher.addURI(authority, "category", categoryDir)
        matcher.addURI(authority, "category/#", categoryItem)
        matcher
    }
    
    by lazy 代码块是Kotlin 提供的一种懒加载技术,代码块中的代码一开始并不会执行,
    只有当uriMatcher变量首次被调用的时候才会执行,
    并且会将代码块中最后一行代码的返回值赋给uriMatcher。
    
    默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。
    如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。 
    而如果你确定初始化将总是发生在与属性使用位于相同的线程, 那么可以使用 LazyThreadSafetyMode.NONE 模式:它不会有任何线程安全的保证以及相关的开销。
    
    可观察属性:
    Delegates.observale()是实现可观察属性赋值后监听的方法;
    相反的是有一个方法叫做Delegates.vetoable()是赋值前判断,满足条件才赋值。
    
    class User {
        var name: String by Delegates.observable("<no name>") {
                property, old, new ->
            println("$old -> $new")
        }
    
        var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
            //如果newValue大于30将不会赋值
            newValue < 30
        }
    }
    
    val user = User()
    user.name = "first"
    user.name = "second"
    user.age = 20
    println("age:" + user.age)
    user.age = 40
    println("age:" + user.age)
    
    打印结果:
    <no name> -> first
    first -> second
    age:20
    age:20
    
    
    将属性储存在映射中:
    
    class User(val map: Map<String, Any?>) {
        val name: String by map // 变量name必须和属性名称相同
        val age: Int     by map // 变量age必须和属性名称相同
    }
    
    val user = User(mapOf(
        "name" to "John Doe",
        "age"  to 25
    ))
    
    println(user.name)
    println(user.age)
    
    打印结果:
    John Doe
    25
    

    [本章完...]

    相关文章

      网友评论

        本文标题:kotlin<第二篇>:类与继承

        本文链接:https://www.haomeiwen.com/subject/afewwrtx.html