2. Kotlin---类和对象

2019-10-26  本文已影响0人  努力生活的西鱼
Kotlin

类定义

Kotlin使用关键字class声明类

class Person {
}

如果一个类没有类体,可以省略花括号。

class Person

主构造函数

一个类可以有一个主构造函数,一个或多个次构造函数。

class Person constructor(firstName:String){
}

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个constructor关键字。

class Person (firstName:String){

}

主构造函数不能包含任何的代码。初始化的代码可以放到init作为前缀的初始化块中。

注意:主构造函数的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用。

class Person (firstName:String){

    init {
        println("FirstName is $firstName");
    }

    var name: String = firstName;

}

次构造函数

类也可以声明前缀为constructor的次级构造函数

class Person {

    constructor(parent:Person) {
        parent.childen.add(this);
    }

}

如果类有主构造函数,每个次构造函数都需要直接或间接地委托给主构造函数。委托到同一个类的另一个构造函数用this关键字。

class Person(val name: String) {
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
}

注意:初始化代码块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次级构造函数的第一条语句,因此所有初始化代码块中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托也会隐式发生,并且仍会执行初始化块。

class Person (firstName: String){

    init {
        println("FirstName is $firstName");
    }

    constructor(firstName: String,lastName: String) : this(firstName) {
        println("firstName is $firstName,lastName is $lastName");
    }

}

val person = Person("W","M");

output:
FirstName is W
firstName is W,lastName is M

如果一个非抽象类没有声明任何构造函数,它会有一个生成的不带参数的主构造函数。构造函数的可见性是public的。如果你不希望类的构造函数是public,你需要声明一个带有非默认可见性的空的构造函数。

class Person private constructor(){

}

继承

Kotlin中所有的类都有一个共同的超类Any

将超类型放到冒号之后

class MeiZi (xingGe:String,zhangXiang:String,shengYin:String):Presons(xingGe,zhangXiang,shengYin){

}

class ShuaiGe (xingGe:String,zhangXiang:String,shengYin:String):Presons(xingGe,zhangXiang,shengYin) {

}

open class Presons constructor(var xingGe:String,var zhangXiang:String,var shengYin:String){

    init {
        println("New 了一个${this.javaClass.simpleName}, ta性格: $xingGe,长相: $zhangXiang,声音: $shengYin");
    }

}

如果派生类有一个主构造函数,其基类型必须用基类的主构造函数参数就地初始化。

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

注意:在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数。

class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

覆盖方法

如果父类中的方法没有标注open,则子类中不允许定义相同签名的函数

子类重写父类的方法,子类方法的前面必须加上override关键字

标记为override的成员本身就是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆盖,使用final关键字

open class Shape {

    open fun draw(){};

    fun fill(){};

}

class Circle : Shape(){

    override fun draw(){};

}

class Rectangle : Shape() {

    final override fun draw() {}

}

覆盖属性

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

open class Shape {

   open val vertexCount:Int = 0;

}

class Circle : Shape(){

    override val vertexCount:Int = 8;

}

你可以用一个var属性覆盖一个val属性,但反之则不行。因为一个val属性本质上声明了一个get方法,而将其覆盖为var只是在子类中额外声明一个set方法。

派生类初始化顺序

在构造派生类的新实例的过程中,第一步完成其基类的初始化,因此发生在派生类的初始化运行逻辑之前。

设计一个基类时,应该避免在构造函数,属性初始化器以及init块中使用open成员。

调用超类实现

派生类中的代码可以使用super关键字调用其父类的函数与属性。

在一个内部类中访问外部类的父类,可以通过由外部类名限定的super关键字来实现。
super@Outer

覆盖规则

如果一个类从它的直接超类继承相同成员的多个实现,它必须覆盖这个成员,并提供自己的实现。为了表示采用从哪个父类型继承的实现,使用由尖括号中父类型名限定的super,如super<Base>

open class Rectangle {
    open fun draw() { /* …… */ }
}

interface Polygon {
    fun draw() { /* …… */ } // 接⼝成员默认就是“open”的
}

class Square() : Rectangle(), Polygon {
    // 编译器要求覆盖 draw():
    override fun draw() {
        super<Rectangle>.draw() // 调⽤ Rectangle.draw()
        super<Polygon>.draw() // 调⽤ Polygon.draw()
    }
}
上一篇下一篇

猜你喜欢

热点阅读