Kotlin 中有四种可见性修饰符:
public:公开,可见性最大,哪里都可以引用。
private:私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见。
protected:保护,相当于 private + 子类可见。
internal:内部,仅对 module 内可见。
相比 Java 少了一个 default 「包内可见」修饰符,多了一个 internal「module 内可见」修饰符。
public
Java 中没写可见性修饰符时,表示包内可见,只有在同一个 package 内可以引用:
package org.kotlinmaster.library;
// 没有可见性修饰符
class User {
}
// 👇 和上面同一个 package
package org.kotlinmaster.library;
public class Example {
void method() {
new User(); // success
}
}
package org.kotlinmaster;
// 👆 和上面不是一个 package
import org.kotlinmaster.library.User;
👆
public class OtherPackageExample {
void method() {
new User(); // compile-error: 'org.kotlinmaster.library.User' is not public in 'org.kotlinmaster.library'. Cannot be accessed from outside package
}
}
package 外如果要引用,需要在 class 前加上可见性修饰符 public 表示公开。
Kotlin 中如果不写可见性修饰符,就表示公开,和 Java 中 public 修饰符具有相同效果。在 Kotlin 中 public 修饰符「可以加,但没必要」。
@hide
在 Android 的官方 sdk 中,有一些方法只想对 sdk 内可见,不想开放给用户使用(因为这些方法不太稳定,在后续版本中很有可能会修改或删掉)。为了实现这个特性,会在方法的注释中添加一个 Javadoc 方法 @hide,用来限制客户端访问:
/**
* @hide 👈
*/
public void hideMethod() {
...
}
但这种限制不太严格,可以通过反射访问到限制的方法。针对这个情况,Kotlin 引进了一个更为严格的可见性修饰符:internal。
internal
internal 表示修饰的类、函数仅对 module 内可见,这里的 module 具体指的是一组共同编译的 kotlin 文件,常见的形式有:
Android Studio 里的 module
Maven project
我们常见的是 Android Studio 中的 module 这种情况,Maven project 仅作了解就好,不用细究。
internal 在写一个 library module 时非常有用,当需要创建一个函数仅开放给 module 内部使用,不想对 library 的使用者可见,这时就应该用 internal 可见性修饰符。
Java 的「包内可见」怎么没了?
Java 的 default「包内可见」在 Kotlin 中被弃用掉了,Kotlin 中与它最接近的可见性修饰符是 internal「module 内可见」。为什么会弃用掉包内可见呢?我觉得有这几个原因:
Kotlin 鼓励创建 top-level 函数和属性,一个源码文件可以包含多个类,使得 Kotlin 的源码结构更加扁平化,包结构不再像 Java 中那么重要。
为了代码的解耦和可维护性,module 越来越多、越来越小,使得 internal 「module 内可见」已经可以满足对于代码封装的需求。
protected
Java 中 protected 表示包内可见 + 子类可见。
Kotlin 中 protected 表示 private + 子类可见。
Kotlin 相比 Java protected 的可见范围收窄了,原因是 Kotlin 中不再有「包内可见」的概念了,相比 Java 的可见性着眼于 package,Kotlin 更关心的是 module。
private
Java 中的 private 表示类中可见,作为内部类时对外部类「可见」。
Kotlin 中的 private 表示类中或所在文件内可见,作为内部类时对外部类「不可见」。
private 修饰的变量「类中可见」和 「文件中可见」:
class Sample {
private val propertyInClass = 1 // 👈 仅 Sample 类中可见
}
private val propertyInFile = "A string." // 👈 范围更大,整个文件可见
private 修饰内部类的变量时,在 Java 和 Kotlin 中的区别:
在 Java 中,外部类可以访问内部类的 private 变量:
public class Outter {
public void method() {
Inner inner = new Inner();
👇
int result = inner.number * 2; // success
}
private class Inner {
private int number = 0;
}
}
在 Kotlin 中,外部类不可以访问内部类的 private 变量:
class Outter {
fun method() {
val inner = Inner()
👇
val result = inner.number * 2 // compile-error: Cannot access 'number': it is private in 'Inner'
}
class Inner {
private val number = 1
}
}
可以修饰类和接口
Java 中一个文件只允许一个外部类,所以 class 和 interface 不允许设置为 private,因为声明 private 后无法被外部使用,这样就没有意义了。
Kotlin 允许同一个文件声明多个 class 和 top-level 的函数和属性,所以 Kotlin 中允许类和接口声明为 private,因为同个文件中的其它成员可以访问:
private class Sample {
val number = 1
fun method() {
println("Sample method()")
}
}
// 👇 在同一个文件中,所以可以访问
val sample = Sample()