来自Kotlin中文网: 接口可见性修饰符

Kotlin 的接口

Kotlin 的接口可以包含抽象方法的声明也可以包含实现。接口中可以有属性,但是属性默认情况下,要么是抽象的,无法赋值;要么是只读变量,同时提供getter。接口中的属性不能有幕后字段field

  1. interface MyInterface {
  2. // 抽象变量
  3. var name: String
  4. // 只读变量,同时提供getter
  5. val propertyWithImplementation: String
  6. get() = "foo"
  7. fun bar()
  8. fun foo() {
  9. println(name)
  10. }
  11. }
  12. class MyClass : MyInterface {
  13. // 在实现类中,实现 name
  14. override var name: String = "Ricky"
  15. // 在实现类中,实现抽象方法
  16. override fun bar() {
  17. println("bar...")
  18. }
  19. }
  20. fun main() {
  21. val myClass = MyClass()
  22. myClass.foo()
  23. myClass.bar()
  24. println(myClass.propertyWithImplementation)
  25. println(myClass.name)
  26. }

接口继承

一个接口可以从其他接口继承,从而既可以提供父接口成员的实现,也可以声明新的函数与属性。

  1. interface Named {
  2. val name: String
  3. }
  4. interface Person : Named {
  5. val firstName: String
  6. val lastName: String
  7. override val name: String get() = "$firstName $lastName"
  8. }
  9. data class Employee(
  10. // 不必实现“name”
  11. override val firstName: String,
  12. override val lastName: String,
  13. val position: Position
  14. ) : Person

解决覆盖冲突

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

  1. interface A {
  2. fun foo() { print("A") }
  3. fun bar()
  4. }
  5. interface B {
  6. fun foo() { print("B") }
  7. fun bar() { print("bar") }
  8. }
  9. class C : A {
  10. override fun bar() { print("bar") }
  11. }
  12. class D : A, B {
  13. override fun foo() {
  14. // 使用<>包含接口名,来指定所实现的方法
  15. super<A>.foo()
  16. super<B>.foo()
  17. }
  18. override fun bar() {
  19. super<B>.bar()
  20. }
  21. }

上面代码中, 类 D 实现了 A和B接口,同时 A 和 B都是实现了相同的方法 foo(),当我们在类D中想同时调用 A和B接口中的 foo() 方法,那么我们可以通过 super<接口名> 的方式调用指定接口的方法。

可见性修饰符

可见性修饰符类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有 可见性修饰符。(getter 总是与属性有着相同的可见性。)

Kotlin 中有四个可见性修饰符:privateprotectedinternalpublic

下面介绍修饰符如何应用到不同类型的声明作用域。

函数、属性和类、对象和接口可以在顶层声明,即直接在包内:

  1. // 文件名:example.kt
  2. package foo
  3. fun baz() { …… }
  4. class Bar { …… }
  • 如果你不指定任何可见性修饰符,默认为 public,这意味着你的声明将随处可见;
  • 如果你声明为 private,它只会在声明它的文件内可见;
  • 如果你声明为 internal,它会在相同模块内随处可见;
  • protected 不适用于顶层声明。

注意:要使用另一包中可见的顶层声明,仍需将其导入进来。

  1. // 文件名:example.kt
  2. package foo
  3. private fun foo() { …… } // 在 example.kt 内可见
  4. public var bar: Int = 5 // 该属性随处可见
  5. private set // setter 只在 example.kt 内可见
  6. internal val baz = 6 // 相同模块内可见

类和接口

对于类内部声明的成员:

  • private :使用private修饰的成员只在本类内部(包含其所有成员)可见;

  • protected:使用protected修饰的成员只在本类内部(包含其所有成员)可见;同时也可以在子类中可见;

  • internal —— 使用 internal 声明的成员在同一个模块中可见;

  • public —— 使用 public 声明的成员可以在任何可以引用到该类的地方使用。

请注意在 Kotlin 中,外部类不能访问内部类的 private 成员。

如果你覆盖一个 protected 成员并且没有显式指定其可见性,该成员还会是 protected 可见性。

例如:

  1. open class Outer {
  2. private val a = 1
  3. protected open val b = 2
  4. internal val c = 3
  5. val d = 4 // 默认 public
  6. protected class Nested {
  7. public val e: Int = 5
  8. }
  9. }
  10. class Subclass : Outer() {
  11. // a 不可见
  12. // b、c、d 可见
  13. // Nested 和 e 可见
  14. override val b = 5 // “b”为 protected
  15. }
  16. class Unrelated(o: Outer) {
  17. // o.a、o.b 不可见
  18. // o.c 和 o.d 可见(相同模块)
  19. // Outer.Nested 不可见,Nested::e 也不可见
  20. }

构造函数

默认情况下所有的构造函数都是 public 的,只要类可以被引用的地方都是可见的。如果要修改主构造函数的可见性,可以在类头中添加 可见性修饰符 + constructor 。如下:

  1. class C private constructor(a: Int) { …… }

代码中的主构造函数是私有的。

局部声明

局部变量、函数和类不能有可见性修饰符。它们只在作用域内有效。

模块

可见性修饰符 internal 意味着该成员只在相同模块内可见。更具体地说, 一个模块是编译在一起的一套 Kotlin 文件:

  • 一个 IntelliJ IDEA 模块;
  • 一个 Maven 项目;
  • 一个 Gradle 源集(例外是 test 源集可以访问 main 的 internal 声明);
  • 一次 <kotlinc> Ant 任务执行所编译的一套文件。

在接口中可以定义属性,但是该属性是抽象的。