Kotlin 的接口
Kotlin 的接口可以包含抽象方法的声明也可以包含实现。接口中可以有属性,但是属性默认情况下,要么是抽象的,无法赋值;要么是只读变量,同时提供getter。接口中的属性不能有幕后字段field
。
interface MyInterface {
// 抽象变量
var name: String
// 只读变量,同时提供getter
val propertyWithImplementation: String
get() = "foo"
fun bar()
fun foo() {
println(name)
}
}
class MyClass : MyInterface {
// 在实现类中,实现 name
override var name: String = "Ricky"
// 在实现类中,实现抽象方法
override fun bar() {
println("bar...")
}
}
fun main() {
val myClass = MyClass()
myClass.foo()
myClass.bar()
println(myClass.propertyWithImplementation)
println(myClass.name)
}
接口继承
一个接口可以从其他接口继承,从而既可以提供父接口成员的实现,也可以声明新的函数与属性。
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName"
}
data class Employee(
// 不必实现“name”
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
解决覆盖冲突
实现多个接口时,可能会遇到同一方法继承多个实现的问题。例如:
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()
}
}
上面代码中, 类 D 实现了 A和B接口,同时 A 和 B都是实现了相同的方法 foo(),当我们在类D中想同时调用 A和B接口中的 foo() 方法,那么我们可以通过 super<接口名>
的方式调用指定接口的方法。
可见性修饰符
可见性修饰符类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有 可见性修饰符。(getter 总是与属性有着相同的可见性。)
Kotlin 中有四个可见性修饰符:private
、 protected
、 internal
和 public
。
下面介绍修饰符如何应用到不同类型的声明作用域。
包
函数、属性和类、对象和接口可以在顶层声明,即直接在包内:
// 文件名:example.kt
package foo
fun baz() { …… }
class Bar { …… }
- 如果你不指定任何可见性修饰符,默认为
public
,这意味着你的声明将随处可见; - 如果你声明为
private
,它只会在声明它的文件内可见; - 如果你声明为
internal
,它会在相同模块内随处可见; protected
不适用于顶层声明。
注意:要使用另一包中可见的顶层声明,仍需将其导入进来。
// 文件名:example.kt
package foo
private fun foo() { …… } // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
private set // setter 只在 example.kt 内可见
internal val baz = 6 // 相同模块内可见
类和接口
对于类内部声明的成员:
private
:使用private
修饰的成员只在本类内部(包含其所有成员)可见;protected
:使用protected
修饰的成员只在本类内部(包含其所有成员)可见;同时也可以在子类中可见;internal
—— 使用internal
声明的成员在同一个模块中可见;public
—— 使用public
声明的成员可以在任何可以引用到该类的地方使用。
请注意在 Kotlin 中,外部类不能访问内部类的 private 成员。
如果你覆盖一个 protected
成员并且没有显式指定其可见性,该成员还会是 protected
可见性。
例如:
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // 默认 public
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a 不可见
// b、c、d 可见
// Nested 和 e 可见
override val b = 5 // “b”为 protected
}
class Unrelated(o: Outer) {
// o.a、o.b 不可见
// o.c 和 o.d 可见(相同模块)
// Outer.Nested 不可见,Nested::e 也不可见
}
构造函数
默认情况下所有的构造函数都是 public
的,只要类可以被引用的地方都是可见的。如果要修改主构造函数的可见性,可以在类头中添加 可见性修饰符 + constructor
。如下:
class C private constructor(a: Int) { …… }
代码中的主构造函数是私有的。
局部声明
局部变量、函数和类不能有可见性修饰符。它们只在作用域内有效。
模块
可见性修饰符 internal
意味着该成员只在相同模块内可见。更具体地说, 一个模块是编译在一起的一套 Kotlin 文件:
- 一个 IntelliJ IDEA 模块;
- 一个 Maven 项目;
- 一个 Gradle 源集(例外是
test
源集可以访问main
的 internal 声明); - 一次
<kotlinc>
Ant 任务执行所编译的一套文件。
在接口中可以定义属性,但是该属性是抽象的。