Skip to content

Instantly share code, notes, and snippets.

@twiceyuan
Last active May 17, 2019 02:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twiceyuan/d88d37c80fb41cfd9eec4bf6c2503b69 to your computer and use it in GitHub Desktop.
Save twiceyuan/d88d37c80fb41cfd9eec4bf6c2503b69 to your computer and use it in GitHub Desktop.
[Kotlin/Java 可见性修饰符] Kotlin 和 Java 在可见性修饰符上的比较 #Kotlin #Java

在 Java 中经常会用到 public/protected/private 来修饰一个成员变量或者方法。public、private 的含义相当明确,前者代表无论何时都可以在任何地方引用这个变量或方法,而后者代表只要在类之外,一概不允许引用这个变量或方法。而 protected 我们一开始被告知它是让成员在包内可见的,然而另外一个特性经常被选择性忽略,就是它能让这个成员可在包外的子类访问。例如我们定义下面这个类:

package com.example.package1;

public class A {
  void defaultMethod() {
    //... 
  }
  
  protected void protectedMethod() {
   	// ... 
  }
}

当在不同的包继承这个类时:

package com.example.package2;

public class AChild extends A {
  void test() {
   	defaultMethod();   // 无法调用
    protectedMethod(); // 可以调用
  }
}

无论是默认不写修饰符,还是写 protected 修饰符,它的包内可见都是很容易被打破的:我只要定义和 A 所在的包路径相同就可以了,这就一定意义上违背了 protected 设计的初衷。Kotlin 中使用了 protected 和 internal 分别做了 Java 中 protected 做的两件事:保证成员的子类可见性和保证模块可见性。protected 的子类可见性和 Java 的 protected 一样,但不会增加对类意外的访问性;而 internal 中对模块可见性让我很疑惑,什么是模块?是包吗?于是我测试了一下:

package com.exmaple.package1

class A {
  internal fun internalFunction() {
  	//...
  }
}
package com.exmaple.package2

class B {
	fun test() {
		A().internalFunction() // 合法
	}
}

经过测试,显然不是。查了 Kotlin in Action 中的解释,internal 的模块是指“一组一起编译的 Kotlin 文件”,可能是一个 IDEA Module、Eclipse 项目、一个 Maven 或者 Gradle 项目。那么一个 module 在哪里记录了哪些文件属于一个 module 呢?经过查看解压打包的 aar 包可以看到包含 kotlin 的项目打包后都有一个 META-INF/library_release.kotlin_module 下保存了 module 相关信息,文件名规则为 [module name]_[build type].kotlin_module。可能有不少人遇到过依赖多个 kotlin module 时这个文件会冲突,因为很多库 module 的名称都是 library,然后一般都会简单的把这个文件 exclude 掉来避免打包错误,其实这种做法是不对但无奈的,因为引用别人的包不方便对这个信息进行修改。如果是自己的 kotlin module 的话,建议使用 Compile Options 来单独配置下编译时 module 的名称来避免冲突:

android {
  //...
  compileOptions {
    kotlinOptions.freeCompilerArgs += ['-module-name', "com.example_group_name.example_artifact_name"]
  }
  //...
}

另外 Kotlin 中 internal 关键字在 Java 上下文中也是无效的,但是还是会有一些方法来避免 Java 调用,例如使用 @JvmName 注解来定义一个在 Java 中不合法的命名,或者直接使用 `` 来定义一个在 Java 中不合法的方法名。这里个人倾向于前者,因为自己 module 内部要用时,使用一些比较奇怪的命名还是不太方便的。

参考文档:

  1. https://ice1000.org/2017/11/12/InternalFucksJava/
  2. https://deskid.github.io/2019/03/05/about-kotlin-module/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment