Kotlin:类与对象(三)

2021-02-08  本文已影响0人  Coralline_xss

本篇内容清单如下:

一共 16 点内容,原文档中更细,为重点篇章,初学有很多陌生概念。有些可能也不是太理解,不知使用场景,如 函数式(SAM)接口;有些新知识点用起来会弥补 Java 不能实现的一些缺憾,感觉非常不错,如 扩展

一、类与继承

1. 类

1.1 类声明
1.2 构造函数
主构造函数
// 主构造函数语法
class Person(name: String) { ... }

// 访问主构造的参数
class Person2(name: String) {
    // 1. 在 类体 内声明的 属性初始化器 中使用 主构造的参数
    val introduction = "My name is $name" 

    // 2. 在 init 初始化块 中使用 主构造的参数
    init {
        println("name = $name")
    } 
}

// 简洁语法:声明属性 && 初始化主构造函数的属性
class Person3(val name: String, var age: Int) { ... } 
次构造函数
class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()

    constructor(name: String, parent: Person) : this(name) {
        parent.add(this)
    }
}

Tips:

1.3 创建类的实例
val person = Person("Coral")

val demo = Demo()
1.4 类成员

类 可以包括:

2. 继承

2.1 覆盖方法
open class Shape {
    
    open fun draw() { // 可被覆盖 }  
    open fun color() { }
    
    fun fill()  { // 不可覆盖,子类也不允许有同签名方法 }               
}

class Circle() : Shape() {
    override fun draw()  { // 覆盖方法 }          
    
    final override fun color() { // 禁止再次覆盖 }
}
2.2 覆盖属性
open class Shape {
    open val vertexCount: Int = 0

    open val borderColor: String = ""
    open val bgColor: String = ""
}

class Rectangle(override val vertexCount: Int =  4) : Shape() {  // 在主构造覆盖属性

    override val borderColor: String = "#000000"
    override var bgColor = "#ffffff"   // 使用 var 覆盖 val 属性
}
2.3 派生类初始化顺序

构造派生类实例过程:

Tips:

2.4 调用超类实现
2.5 覆盖规则
    open class R1 {
        open fun draw() {  }
    }

    // Tips:接口成员默认是 "open" 的
    interface P1 {
        fun draw() {  }
    }

    // Tips:继承父类,必须要带上父类的构造方法,有参或无参构造
    class S1 : R1(), P1 {
        override fun draw() {
            super<P1>.draw()  // 调用 P1.draw()
            super<R1>.draw()  // 调用 R1.draw()
        }
    }

3. 属性

3.1 幕后字段

举个例子,就很容易知道幕后字段如何使用,并且对比 Java 的使用场景,就知道 幕后字段 使用场景也非常多。

var counter = 0 
    set(value) {
        this.counter = value
    }

上述代码运行后,会出现 stackOverflow,这是为啥?可转换为 Java 代码,set() 里面赋值的那句,直接等价于重新调用了 set() 方法,如此就出现了 自己调用自己,就出现栈溢出错误了。
为了解决上述问题,便可以使用 幕后字段

关于某后字段总结如下:

// 正确写法
var counter = 0 
    get() = field
    set(value) {
        field = value
    }

// 以下 isEmpty 没有幕后字段,因为操作的不是字段本身
val isEmpty: Boolean
    get() = this.size == 0
    set(value) {
        this.size = 0
    }
3.2 幕后属性
    //  _table 为 幕后属性,仅内部操作,table 为对外访问属性
    private var _table: Map<String, Int>? = null

    public val table: Map<String, Int>
        get() {
            if (_table == null) {
                _table = HashMap()  // 类型参数已推断出
            }
            return _table ?: throw AssertionError("Set to null by other thres")
        }

备注:一开始不太理解幕后字段 & 幕后属性这两个概念的,详解与使用场景可查看:https://www.jianshu.com/p/c1a4c04eb33c

3.3 编译期常量
// 对比 Java
private final String a = "A";
private final static String b = "B";
public  final static String c = "C";

// 转换为 Kotlin
val a = "A"
companion object {
    val b = "B"
    const val c = "C"   // public 常量才能用 const 标记
}

3. 接口

3.1 函数式(SAM)接口

TODO 暂时不理解定义与使用场景。

4. 可见性修饰符

省略。

5. 扩展

6. 类的几种表现形式

6.1 伴生对象

创建一个类实例,但是不用显式声明新的子类,使用 对象表达式对象声明 处理。

6.2 对象表达式
6.3 对象声明

上述三者之间的语义差别:

代码 DEMO:

    // 1. 对象表达式
    // Demo01:创建一个继承自 某个/某些 类型的匿名类的对象
    abstract class BaseAdapter {
        abstract fun getCount(): Int
    }

    class MyListView {
        // 初始化一个 adapter,对象表达式 object : BaseAdapter
        var adapter: BaseAdapter = object : BaseAdapter() {
            override fun getCount(): Int = 3
        }
    }

   // 2. 对象表达式   
    class DataProvider

    // 对象表达式,object 类名 {}   可以在另一个文件
    object DataManager {
        fun register(provider: DataProvider) {
            //
        }

        val providers: Collection<DataManager>
            get() {
                TODO()
            }
    }

    fun testSingleton() {
        DataManager.register(DataProvider())
    }

    /**
     * 3. 伴生对象
     * - 类内部的对象声明 可以用 companion 关键字标记。
     */
    class MyCompanion {
        companion object Factory {
            fun create(): MyCompanion = MyCompanion()
            
            val globalCount: Int = 0
        }
    }

    // Tips:伴生对象的成员 可通过 只是用类名 作为限定符来调用
    val instance = MyCompanion.create()
    val count = MyCompanion.globalCount

6.4 抽象类
open class Polygon {
    open fun draw() {}
}

abstract class Rectangle : Polygon() {
    abstract override fun draw()
}
6.5 数据类

编译器会自动从主构造函数中声明的属性导出/生成以下成员:

为生成合理的代码,数据类满足条件:

6.6 密封类
6.7 嵌套类与内部类
6.8 枚举类
enum class Color(val rgb: Int) {
                RED(0xFF0000),
                GREEN(0x00FF00),
                BLUE(0x0000FF),
}
6.9 内联类

仅在 Kotlin 1.3 版本之后才可用,目前处于 Alpha 版本。

内联类成员限制:

7. 泛型

TODO

8. 委托

8.1 委托属性

有些属性类型,随每次可手动调用,但是能实现一次并放入一个库会更好。例如包括:

为了涵盖上述情况及其他,Kotlin 支持 委托属性:

class Demo {
  var p: String by Delegate()
}

语法:val/var <属性名>: <类型> by <表达式>by 后面的就是该 委托

更多详见:https://mp.weixin.qq.com/s/BD1zT80IADDZS4CAxmooPg

上一篇 下一篇

猜你喜欢

热点阅读