Kotlin:主次构函数、覆盖属性和伴生对象给你带来什么

一、类

  1. Kotlin使用关键字class声明类
  2. 类的声明由类名、类头(指定参数类型、主构函数等)和由大括号包围的类体构成
  3. 类头和和类体都是可选的,如果没有类体,可以省略花括号
//1.使用关键字class声明类
class Invoice {
}
//3.类头和类体都都是可选,没有类体可以省略花括号
class Empty

二、构造函数

  1. 在Kotlin中一个类可以有一个主构造函数一个或多个次构造函数
  2. 主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。如果主构造函数没有任何注解或可见性修饰符constructor关键字可省略,否则是必须的;
  3. 主构函数不能包含任何代码,初始化代码放在以init关键字作为前缀的初始化块中
  4. 主构函数的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用
  5. 与普通属性一样,主构函数中声明的属性也可以是可变(var )或者只读(val )
//2.类名Customer后,主构造函数(firstName: String) ,无注解和可见性修饰符,constructor关键字省略
//5.主构函数中声明属性name是只读的
class Customer(val name: String) {
    //3.主构函数无代码,放在init关键字初始化块中
    init {
        logger.info("Customer initialized with value ${name}")
    }
    //4.主构函数参数可以类体内声明的属性初始化器中使用,或者初始化块中使用
    val customerKey = name.toUpperCase()
}

三、次构造函数

  1. 次构造函数声明前缀有constructor
  2. 如果类有一个主构造函数,每个次构造函数可以使用this关键字直接委托或者通过别的次构造函数间接委托给主构造函数
  3. 如果一个抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数,可见性是public。如果不希望类有一个公有构造函数,需要声明一个带有非默认可见性的主构造函数;
class Person {
    //1.次构造函数声明前缀有constructor
    constructor(parent: Person) {
        parent.children.add(this)
    }
}
class Person(val name: String) {
    //2.使用this关键字,直接委托给主构造函数
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}
//3.主动声明一个公有构造函数,“覆盖 ”没有声明任何主次构造函数默认生成的不带参数的主构造函数
class DontCreateMe private constructor () {
}

四、创建类的实例

  1. 创建类的实例,像普通函数一样调用构造函数,Kotlin中没有new关键字
  2. 类的成员包含:构造函数和初始化块,函数,属性,嵌套类和内部类,对象声明
//1.像普通函数一样调用构造函数,没有new关键字
val invoice = Invoice()
val customer = Customer("Joe Smith")

五、继承

  1. 在Kotlin中所有类都有一个共同的超类Any,没有声明超类型声明的都是默认超类Any;
  2. Any不是java.lang.Object:除了equals()、hashCode()和toString()外没有任何成员;
  3. 要声明一个显式的超类型,把类型放在类头的冒号之后
  4. 如果类有一个主构函数,其基类可以(并且必须)用(基类型的)主构函数参数就地初始化
  5. 如果没有主构函数,每个次构函数必须使用super关键字初始化其类型,或委托给一个构造函数初始化
//1.从 Any 隐式继承
class Example
//2,3.Derived继承超类Base,超类型放在类头的冒号之后
open class Base(p: Int)
//4.Base有一个主构函数,Derived必须使用Base的主构函数初始化
class Derived(p: Int) : Base(p)
//5.View没有主构函数,MyView的次构函数使用super委托给另一个构造函数;
class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

Java对比:类上open标注与Java中final相关,它允许其它类从这个类继承。默认情况下,Kotlin所有的类都是final;

六、覆盖方法

  1. Kotlin与Java不同,需要显示使用open关键字标注可覆盖的成员使用override关键字覆盖后的成员
  2. 标记为override的成员本身是开放的,如果你想禁止再次覆盖使用final关键字
//1.open显示标注可继承的类Base,可覆盖的方法v(),否在默认为final
open class Base {
    open fun v() {}
    fun nv() {}
}
class Derived() : Base() {
    //1.override标注覆盖方法,否则编译出错
    override fun v() {}
}
open class AnotherDerived() : Base() {
     //2.final关键字禁止本身开放的override成员
     final override fun v() {}
}

七、覆盖属性

  1. 和方法覆盖类似,在超类中声明后再派生类中重新声明的属性必须以override开头,并且必须具有兼容类型;
  2. 每个声明的属性可以由具有初始化器的属性或者具有getter方法的属性覆盖;
  3. 可以使用一个var属性覆盖一个val属性,反之则不行。因为val本质声明了一个getter方法,覆盖为var只是在子类中额外声明一个setter方法
open class Foo {
    open val x: Int get() { …… }
}
//1.以override开头的x,覆盖了Foo中open开头的属性x
class Bar1 : Foo() {
    override val x: Int = ……
}

interface Foo {
    val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
    //2.var属性count覆盖了val属性count,本质增加额外的setter方法
    override var count: Int = 0
}

八、调用超类实现

  1. 派生类可以使用super关键字调用超类的函数与属性访问器的实现;
  2. 在一个内部类访问外部类的超类,可以由外部类名限定的super关键字实现:super@Outer;
open class Foo {
    open fun f() { println("Foo.f()") }
    open val x: Int get() = 1
}
class Bar : Foo() {
    override fun f() {
        //1.super关键字调用超类的函数
        super.f()
        println("Bar.f()")
    }

    //1.super关键字调用属性访问器
    override val x: Int get() = super.x + 1
}

class Bar : Foo() {
    override fun f() { /* …… */ }
    override val x: Int get() = 0

    inner class Baz {
        fun g() {
            //2.调用 Foo 实现的 f()
            super@Bar.f()
            //2.使用 Foo 实现的 x 的 getter
            println(super@Bar.x)
        }
    }
}

九、覆盖规则

  1. 在Kotlin中,如果一个类从它的直接超类继承相同的成员的多个实现必须覆盖这个成员并提供自己的实现(也许用继承来的其中之一);
  2. 为了表示采用从哪个超类型继承的实现,使用尖括号中超类型名限定的super
open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    // 接口成员默认就是“open”的
    fun f() { print("B") } 
    fun b() { print("b") }
}

class C() : A(), B {
    // 1、2.编译器要求覆盖 f(),分表使用限定符调用A.f()和B.f()
    override fun f() {
        super<A>.f()
        super<B>.f() 
  }
}

十、抽象类

  1. 类和其中的某些成员可以声明为abstract抽象成员在本类中可以不实现,也并不需要open标注抽象类和函数;
  2. 可以用一个抽象成员覆盖一个非抽象的开放成员
open class Base {
    open fun f() {}
}

//1.Derived类使用abstract声明为抽象类
abstract class Derived : Base() {
    //2.用一个抽象成员覆盖一个非抽象的开放成员
    override abstract fun f()
}

十一、伴生对象

Java对比:Kotlin中类没有静态方法,大多数情况下,建议简单的使用包级函数。或在类内声明一个伴生对象,就可以像Java/C#调用静态方法相同的语法来调用其它成员,只使用类名作为限定符;

1.新技术,新未来!尽在1024工场。时刻关注最前沿技术资讯,发布最棒技术博文!(甭客气!尽情的扫描或者长按!)

《Kotlin:主次构函数、覆盖属性和伴生对象给你带来什么》 1024工场服务号

2.加入“Kotlin开发”QQ讨论群,一起学习一起Hi。(甭客气!尽情的扫描或者长按!)

《Kotlin:主次构函数、覆盖属性和伴生对象给你带来什么》 Kotlin开发群

    原文作者:1024工场
    原文地址: https://www.jianshu.com/p/d723e0de618c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞