第4章 类对象和接口

4.1 定义类继承结构

4.1.1 Kotlin中的接口

//接口定义
interface Clickable {
    fun click()
}
//实现接口
class Button:Clickable{
    override fun click() = println("I was clicked")
}

接口的方法可以有一个默认实现。

interface Clickable {
    fun click()
    fun showOff() = println("I'm clickable!") //带默认实现的方法
}

假设存在同样定义了一个showOff方法并且有如下实现的另一个接口。

interface Focusable {
    fun setFocus(b: Boolean) =
            println("I ${if (b) "got" else "lost"} focus.")
    fun showOff() = println("I'm focusable!")
}

类实现这两个接口,如果没有显式实现showOff,将会得到如下编译错误:

4.1.2 open、final和abstarc修饰符:默认为final

Java的类和方法默认是open的,而kotlin中默认都是final的。如果你想允许创建一个类的子类, 需要使用open修饰符来标示这个类。此外,需要给每一个可以被重写的属性或方法添加open修饰符。

如果重写了一个基类或者接口的成员,重写了的成员同样默认是open的,如果你想改变这一行为,阻止你的子类重写你的实现,可以显式地将重写的成员标注为final。

抽象成员始终是open的,所以不需要显式地使用open修饰符。

4.1.3 可见性修饰符:默认为public

4.1.4 内部类和嵌套类:默认是嵌套类

与Java不同,Kotlin的嵌套类不能访问外部类的实例。

4.1.5 密封类:定义受限的类继承结构

总是不得不添加一个默认分支很不方便。更重要的是,如果你添加一个新的子类,编译器并不能发现有地方改变了,如果你忘记了添加一个新分支,就会选择默认的选项,这可能导致潜在的bug。

Kotlin为这个问题提供了一个解决方案:sealed类。为父类添加一个sealed修饰符,对可能创建的子类做出严格的限制。所有的直接子类必须嵌套在父类中。

sealed修饰符隐含的这个类是一个open类,不再需要显式地添加open修饰符。

4.2 声明一个带非默认构造方法或属性的类

4.2.1 初始化类:主构造方法和初始化语句块

这段被括号围起来的语句块就叫作主构造方法,它主要有两个目的:表明构造方法的参数,以及定义使用这些参数初始化的属性。

constructor关键字用来开始一个主构造方法或从构造方法的声明。init关键字用来引入一个初始化语句块。因为主构造方法有语法限制,不能包含初始化代码,所以要使用初始化语句块。一个类中可以声明多个初始化语句块。

构造方法参数_nickname中的下划线用来区分属性的名字和构造方法参数的名字。另一个可选方案是使用同样的名字,通过this来消除歧义。

在这个例子中,不需要把初始化代码块放在初始化语句块中,因为它可以与nickname属性的声明结合。如果主构造方法没有注解或可见性修饰符,同样可以去掉constructor关键字。

如果不想类被其他代码实例化,必须把构造方法标记为private

4.2.2 构造方法:用不同的方式来初始化父类

4.2.3 实现在接口中声明的属性

4.2.4 通过getter或setter访问幕后字段

4.2.5 修改访问器的可见性

4.3 编译器生成的方法:数据类和类委托

4.3.1 通用对象方法

对象相等性:equals()

Hash容器:hashCode()

4.3.2 数据类:自动生成通用方法的实现

数据类和不可变性:copy()方法

虽然数据类的属性并没有要求是val,但是强烈推荐只使用只读属性,让数据类的实例不可变。

为了让使用不可变对象的数据类变得更容易,Kotlin编译器为它们多生成了一个方法:一个允许copy类的实例的方法,并在copy的同时修改某些属性的值。创建副本通常是修改实例的好选择:副本有着单独的生命周期而且不会影响代码中引用原始实例的位置。

4.3.3 类委托:使用“by”关键字

4.4 “object”关键字:将声明一个类与创建一个实例结合起来

Kotlin中object关键字在多种情况下出现,但是它们都遵循同样的核心概念:这个关键字定义一个类并同时创建一个实例。让我们来看看使用它的不同场景:

  • 对象声明时定义单例的一种方式。

  • 伴生对象可以持有工厂方法和其他与这个类相关,但在调用时并不依赖类实例的方法。它们的成员可以通过类名来访问。

  • 对象表达式用来替代Java的匿名内部类。

4.4.1对象声明:创建单例易如反掌

对象声明通过object关键字引入。一个对象声明可以非常高效地以一句话来定义一个类和一个该类的变量。

与类一样,一个对象声明也可以包含属性、方法、初始化语句块等的声明。唯一不允许的就是构造方法。与普通类的实例不同,对象声明在定义的时候就立即创建了,不需要在代码的其他地方调用构造方法。为对象声明定义一个构造方法是没有意义的。

对象声明同样可以继承自类和接口。

同样可以在类中声明对象。

4.4.2 伴生对象:工厂方法和静态成员的地盘

Kotlin中的类不能拥有静态成员。在类中定义的对象使用关键字:companion,就获得了直接通过容器类名称来访问这个对象的方法和属性的能力。

伴生对象可以访问类中的所有private成员,包括private构造方法,它是实现工厂模式的理想选择。

4.4.3 作为普通对象使用的伴生对象

伴生对象是一个声明在类中的普通对象。它可以有名字,实现一个接口或者有扩展函数或属性。

如果你有一个函数使用抽象方法来加载实体,可以传给它Person对象。

伴生对象扩展

4.4.4 对象表达式:改变写法的匿名内部类

最后更新于

这有帮助吗?