Kotlin自学之旅(三)类和接口、继承

类的声明

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

//一个类的完整声明
class Person(name: String) {
     val name  = name
}

//省略类体,在构造方法中声明属性,添加默认参数
class Person(val name:string = "xiaoming")

构造方法

在 Kotlin 中的一个类可以有一个主构造方法和一个或多个次构造方法。主构造方法是类头的一部分:它跟在类名(和可选的类型参数)后。
Kotlin引入了constructorinit两个新的关键字,constructor用于一个主构造方法或者次构造方法的声明,init用来引入一个初始化语句块。init主要用来和主构造方法一起使用,因为主构造方法不能包括初始化代码。如果属性用相应的构造方法参数初始化,可以通过把valvar关键字加在参数钱的方式简化。所以以下三种声明方式都是相同的:

class User constructor(_nickname: String) {
    val nackname: String
    init {
        nackname = _nickname
    }
}

class User constructor(_nickname: String) {
    val nackname = _nickname
}

class User(val nickname:String)

次构造方法

类也可以通过constructor声明更多的次构造方法。如果类有一个主构造函数,每个次构造函数需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:
请注意,初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块:

/* 输出:
*    Init block
*    Constructor
*/
fun main(args: Array<String>) {
    Constructors(5)
}

class Constructors(str :String) {
    init {
        println("Init block")
    }

    constructor(i: Int):this(i.toString()){
        println("Constructor")
    }
}

属性

在Java中,字段和其访问器的组合常常被称为属性,而在Kotlin中,属性是头等的语言特性,完全代替了字段和访问器方法。在类中声明一个属性和声明一个变量一样:属性可以用关键字var 声明为可变的,否则使用只读关键字val
要使用一个属性,只要用名称引用它即可,就像 Java 中的字段:

fun main(args: Array<String>) {
    val xiaoming = Person("xiaoming",false)
    println(xiaoming.name)
    println(xiaoming.isMarried)
}

class Person(val name:String,var isMarried: Boolean)

自定义访问器

声明一个属性的完整语法是

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

其初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器 (或者从其 getter 返回值,如下文所示)中推断出来,也可以省略。
我们可以编写自定义的访问器,非常像普通函数,刚好在属性声明内部。

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}

属性isSquare不需要字段来保存它的值,它的值是每次访问属性的时候计算出来的。


可见性修饰符

总的来说,Kotlin中的可见性修饰符和Java中的类似。同样可以使用publicprotectedprovide修饰符,但是默认的可见性是不一样的:如果省略了修饰符,声明就是public。
Kotlin中没有Java中的默认可见性——包私有,而有一个新的修饰符internal,表示只在模块内可见。一个模块就是一组一起编译的Kotlin文件。以下是各个修饰符的可见范围:

修饰符类成员顶层声明
private类成员可见文件内可见
protected和 private一样 + 在子类中可见
internal模块中可见模块中可见
public所有地方可见所有地方可见

继承

在 Kotlin 中所有类都有一个共同的超类Any,这对于没有超类型声明的类是默认超类。Kotlin在类名后面使用冒号代替了Java中的extendsimplements关键字。和Java一样,一个类可以实现任意多个接口,但只能继承一个类。

open class Base(p: Int)

class Derived(p: Int) : Base(p)

类上的 open 标注与 Java 中 final 相反,它允许其他类从这个类继承。默认情况下,在 Kotlin 中所有的类都是 final
如果派生类有一个主构造函数,其基类型可以(并且必须) 用基类的主构造函数参数就地初始化。
如果类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:

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

覆盖方法

Kotlin中方法也是默认为final的,你需要给每一个可以被重写的方法添加open修饰符,同时,你必须在子类覆盖父类的方法时在前面添加override修饰符。如果没有标注 open ,则子类中不允许定义相同签名的方法, 不论加不加 override。在一个没有用 open 标注的类中,开放成员是禁止的。在派生类中的代码中,可以使用super关键字调用其超类的函数与属性访问器的实现。

open class Base {
    open fun v() {}
    fun nv() {}
}
class Derived: Base() {
    override fun v() {
        super.v()
        nv()
    }
}

覆盖属性

属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。每个声明的属性可以由具有初始化器的属性或者具有 getter 方法的属性覆盖。
你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。

open class Foo {
    open val x: Int get() { …… }
}

class Bar1 : Foo() {
    override val x: Int = ……
}

接口

Kotlin 的接口与 Java 8 类似,既包含抽象方法的声明,也包含实现。与抽象类不同的是,接口无法保存状态。它可以有属性但必须声明为抽象或提供访问器实现。
使用关键字 interface 来定义接口:

interface MyInterface {
    fun bar()
    fun foo() {
      // 可选的方法体
    }
}

一个类或者对象可以实现一个或多个接口。

class Child : MyInterface {
    override fun bar() {
        // 方法体
    }
}

你可以在接口中定义属性。在接口中声明的属性要么是抽象的,要么提供访问器的实现。在接口中声明的属性不能有幕后字段(backing field),因此接口中声明的访问器不能引用它们。

interface MyInterface {
    val prop: Int // 抽象的

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
}

覆盖冲突

实现多个接口时,可能会遇到同一方法继承多个实现的问题。就像下方:

interface A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

Kotlin规定,当子类中有一个方法在父类和接口中有多个实现的话,子类必须覆盖这个方法。在覆盖的方法内子类可以使用super<>语法直接调用父类或接口中的方法。


总结

本文简单的介绍了Kotlin中类和接口的声明与定义,以及两者的继承规则。涉及到的关键字有:

关键字说明
class声明一个类
final禁止成员覆盖
open允许一个类子类化或覆盖成员
override将一个成员标记为超类成员的覆盖
private将一个声明标记为在当前类或文件中可见
protected将一个声明标记为在当前类及其子类中可见
public将一个声明标记为在任何地方可见
super引用一个方法或属性的超类实现,在次构造函数中调用超类构造函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值