我们在 Kotlin 中发现了 object 这个东西,我以前就一直对这个东西很好奇,不知道这是个什么玩意儿。
object ?难道又一个对象?
之前有人写过这样的代码,表示很不解,一个接口类型的成员变量,访问外部类的成员变量 name。这不是理所应当的么?
interface Runnable { fun run()}class Test { private val name: String = "nanchen" object impl : Runnable { override fun run() { // 这里编译器会报红报错。对 name println(name) } }}
即使查看 Kotlin 官方文档,也有这样一段描述:
Sometimes we need to create an object of a slight modification of some class, without explicitly declaring a new subclass for it. Java handles this case with anonymous inner classes. Kotlin slightly generalizes this concept with
object expressions and object declarations.
核心意思是:Kotlin 使用 object 代替 Java 匿名内部类实现。
很明显,即便如此,这里的访问应该也是合情合理的。从匿名内部类中访问成员变量在 Java 语言中是完全允许的。
这个问题很有意思,解答这个我们需要生成 Java 字节码,再反编译成 Java 看看具体生成的代码是什么。
public final class Test { private final String name = "nanchen"; public static final class impl implements Runnable { public static final Test.impl INSTANCE; public void run() { } static { Test.impl var0 = new Test.impl(); INSTANCE = var0; } }}public interface Runnable { void run();}
静态内部类!确实,Java 中静态内部类是不允许访问外部类的成员变量的。但,说好的 object 代替的是 Java 的匿名内部类呢?那这里为啥是静态内部类。
这里一定要注意,如果你只是这样声明了一个object,Kotlin认为你是需要一个静态内部类。而如果你用一个变量去接收object表达式,Kotlin认为你需要一个匿名内部类对象。
因此,这个类应该这样改进:
interface Runnable { fun run()}class Test { private val name: String = "nanchen" private val impl = object : Runnable { override fun run() { println(name) } }}
为了避免出现这个问题,谨记一个原则:如果 object 只是声明,它代表一个静态内部类。如果用变量接收 object 表达式,它代表一个匿名内部类对象。
讲到这,自然也就知道了 Kotlin 对 object 的三个作用:
-
简化生成静态内部类
-
生成匿名内部类对象
-
生成单例对象
咳咳,说了那么多,到底和 @JvmStatic 和 @JvmField 有啥关系呢?
实际上,目前我们大多数的 Android 项目都是 Java 和 Kotlin 混编的,包括我们的项目在内也是如此。所以我们总是免不了 Java 和 Kotlin 互调的情况。我们可能经常会在代码中这样编写:
object Test1 { val NAME = "nanchen" fun getAge() = 18}
在 Java 中会调用是这样的:
System.out.println("name:"+Test1.INSTANCE.getNAME()+",age:"+Test1.INSTANCE.getAge());
作为强迫症重度患者的我,自然是无法接受上面这样奇怪的代码。所以我强烈建议大家在 object 和 companion object 中分别为变量和方法增加上 @JvmField 和 @JvmStatic 注解。
object Test1 { @JvmField val NAME = "nanchen" @JvmStatic fun getAge() = 18}
这样外面 Java 调用起来就好看多了。