美文网首页Kotlin
Kotlin类与继承、属性

Kotlin类与继承、属性

作者: 漆先生 | 来源:发表于2021-03-26 09:34 被阅读0次

    一、类

    Kotlin 中使⽤关键字 class 声明类。类声明由类名、类头(指定其类型参数、主构造函数等)、类体构成。
    类头与类体都是可选的;如果⼀ 个类没有类体,可以省略花括号。

    class Empty//省掉了类头、类体
    

    二、构造函数

    ⼀个类可以有⼀个主构造函数以及⼀个或多个次构造函数

    1.主构造函数

    • 主构造函数没有任何注解或者可⻅性修饰符,可以省略这个 constructor 关键字。
    • 主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
    • 初始化块与属性初始化器的执行顺序按照类体中的顺序
    • 主构造的参数可以在初始化块和属性初始化器中使用
    • 主构造函数中声明的属性可以是可变的(var)或只读的(val)。
    class InitOrderDemo(val lastName: String, var age: Int) {
        val firstProperty =
            "First property: $lastName".also(::println)
    
        init {
            println("First initializer block that prints ${lastName}")
        }
    
        val secondProperty =
            "Second property: ${lastName.length + age++}".also(::println)
    
        init {
            println("Second initializer block that prints ${lastName.length + age}")
        }
    }
    

    2.次构造函数

    在类体中用constructor声明。

    • 如果类有⼀个主构造函数,每个次构造函数需要委托给主构造函数,使用this,可以直接委托或者通过别的次构造函数间接委托。
    • 初始化块会成为主构造函数的⼀部分。委托给主构造函数会作为次构造函数的第⼀条语句,因此所有初始化块与属性初始化器都会在次构造函数体之前执行。
    • 即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块
    class Person(val name: String) {
        var children: MutableList<Person> = mutableListOf()
    
        constructor (name: String, parent: Person) : this(name) {
            parent.children.add(this)
        }
    }
    
    class Constructors {
        init {
            println("Init block")
        }
    
        constructor(i: Int) {
            println("Constructor")
        }
    }
    

    另外,⼀个非抽象类没有声明任何(主或次)构造函数,它会有⼀个生成的不带参数的主构造函数。可见性默认public
    如果主构造函数的所有的参数都有默认值,编译器会生成 ⼀个额外的无参构造函数

    class Customer(val customerName: String = "")
    

    三、创建类的实例

    像普通函数⼀样调⽤构造函数,不需要new

    1.类成员

    • 构造函数与初始化块
    • 函数属性
    • 嵌套类与内部类
    • 对象声明

    2.继承

    在 Kotlin 中所有类都有⼀个共同的超类 Any(隐式继承),类似java的Object

    • 默认情况下,Kotlin 类是最终(final)的,用 open 关键字标记才能被集成
    • 类头中把超类型放到冒号之后
    • 如果派生类有⼀个主构造函数,其基类必须用派生类主构造函数的参数就地初始化。
    open class Base(p: Int) 
    class Derived(p: Int) : Base(p)
    

    派生类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其基类型,或委托给另⼀个构造函数做到这⼀点

    class MyView : View {
        constructor(context: Context) : super(context)
        constructor(context: Context, attr: AttributeSet) : this(context)
    }
    

    3.覆盖方法

    • 基类和被覆盖方法否需要加上open标记
    • 覆盖方法加上override标记
    • 禁止再次覆盖,使⽤final关键 字
    open class Rectangle() : Shape() {
        final override fun draw() { /*……*/ }
    }
    

    4.覆盖属性

    属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。

    • 每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖
    • 你也可以用⼀个 var 属性覆盖⼀个 val 属性,但反之则不行
    interface Shape {
        val vertexCount: Int
    }
    
    class Rectangle(override val vertexCount: Int = 4) : Shape
    
    class Polygon : Shape {
        override var vertexCount: Int = 0 // 以后可以设置为任何数
    }
    

    5.派生类初始化顺序

    先完成其基类的初始化,再完成派生类的初始化。
    基类构造函数执行时,派生类中声明或覆盖的属性都还没有初始化。如果在基类初始化逻辑中(直接或通过 另⼀个覆盖的 open 成员的实现间接)使用了任何⼀个这种属性,那么都可能导致不正确的行为或运行时故障。设计⼀ 个基类时,应该避免在构造函数、属性初始化器以及 init 块中使用 open 成员

    6.调用超类实现

    • 用 super 关键字调用其超类的函数与属性访问器的实现
    • 在⼀个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@外部类的类名,如super@Outer

    7.覆盖规则

    如果⼀个类从它的直接超类继承相同成员的多个实现,为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super<Base> :

    open class Rectangle {
        open fun draw() { /* …… */ }
    }
    
    interface Polygon {
        fun draw() { /* …… */ }// 接⼝成员默认就是“open”的
    }
    
    class Square() : Rectangle(), Polygon {
        override fun draw() {
            super<Rectangle>.draw() // 调用 Rectangle.draw()
            super<Polygon>.draw() // 调用 Polygon.draw() }
        }
    }
    

    8.抽象类

    用abstract声明,不再需要open

    9.伴生对象

    companion关键字标记,该伴生对象的成员可通过只使用类名作为限定符来调用:

    class MyClass {
        companion object Factory {
            fun create(): MyClass = MyClass()
        }
    }
    val instance = MyClass.create()
    

    四、属性

    1.声明属性

    属性既可以用关键字 var 声明为可变的,也可以用关键字 val 声明为只读的。

    2.Getters 与 Setters

    声明⼀个属性的完整语法如下,其初始器(initializer)、getter 和 setter 都是可选的

        var <propertyName>[: <PropertyType>] [= <property_initializer>] 
            [<getter>] 
            [<setter>]
    

    用了幕后字段field,需要使用初始化器。没用幕后字段使用初始化器,编辑器报错:Initializer is not allowed here because this property has no backing field
    ⾃定义的 getter setter 如下所示:

    var stringRepresentation: String
        get() = this.toString()
        set(value) {
            setDataFromString(value) // 解析字符串并赋值给其他属性
        }
    
    var counter = 0
        set(value) {
            field = if (value >= 0) value else -1
        }
    

    1.编辑器常量

    可以使用 const 修饰符将其标记为编译期常量。const var 等于public static final 默认是pubic,可以指定

    • 位于顶层或者是 object 声明 或 companion object 的⼀个成员
    • 以 String 或原生类型值初始化
    • 没有⾃定义 getter

    2.延迟初始化属性与变量

    lateinit 修饰符标记该属性,只能用于在类体中的属性,.isInitialized,判定属性或者变量是否被初始化

    相关文章

      网友评论

        本文标题:Kotlin类与继承、属性

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