关于在Kolint中储存常量,这篇短文讲述了一些可供选择的方案,再者,提出了一些可能会吸引人去踏入的陷阱。但在此之前,让我们先聊一聊被编译成Java后的Kotlin
Kotlin的魅力之一就是你能很容易地将一些复杂的代码简单化,让编译器去代替你做繁杂的工作。 data class
就是一个很好的例子,短短一行代Kotlin码替代了数十行Java代码。
但是能力越大,责任越大。我们很容易就会让Kotlin编译器产生一些本来就可以被简化(suboptimal)的字节码(bytecode),尤其是做安卓开发的。面对众多的类、方法和对象分配,需要意识到会不会出现上述情况。同时JetBrains也提供给了开发者一些集成到了AndroidStudio(当然还有IntelliJ IDEA)反编译工具(Kotlin编译成Java),帮助我们了解和优化代码本身
关于decompile的链接在文末,不过多赘述
接下来讲到
关于顶层常量,当然变量和方法都可以定义在顶层
Kotlin中没有static关键字,如果你想在类中声明静态方法或属性,就要把他们放在companion object(伴生对象)中
class Constants {
companion object {
val FOO = "foo"
}
}
复制代码
当要在其它地方用到的时候,可以像在Java那样 Constants.FOO
现在看一看反编译工具生成对应的Java代码(有简化)
public final class Constants {
@NotNull
private static final String FOO = "foo";
public static final Constants.Companion Companion = new Constants.Companion((DefaultConstructorMarker)null);
public static final class Companion {
@NotNull
public final String getFOO() {
return Constants.FOO;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
复制代码
注意到很重要的一点: Constants.FOO
被编译成Java的 Constants.Companion.getFOO()
,看起来很不优雅,接下来的方法可以避免这个情况
companion object
属性,或 object
属性 把上面的Kotlin代码改一改,变成
class Constants {
companion object {
const val FOO = "foo"
}
}
复制代码
生成对应的Java代码
public final class Constants {
@NotNull
public static final String FOO = "foo";
public static final Constants.Companion Companion = new Constants.Companion((DefaultConstructorMarker)null);
public static final class Companion {
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
复制代码
常量FOO原来对应的 getter
不见了,FOO的访问权限从 private
变成了 public
,于是在Java中可以直接 Constants.FOO
。但是, Companion
这个没用的类依然存在。接下来是另一个变通方法
const val
的本质可以类比C语言的 #define
定义常量
把上面const去掉,给FOO加上JvmField注解 生成的Java代码原文没给
class Constants {
companion object {
@JvmField val FOO = Foo() //Foo()是为了说明不限定于原始类型
}
}
复制代码
生成的Java代码基本和const val的没区别,有一个重要区别就是,访问 const val
的常量时,会变成内联常量, @JvmField
注解的常量则不会,看下面代码
fun main(args: Array<String>) {
println(Constants.FOO)
}
复制代码
编译成Java后 @JvmField
注解版本
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
Foo var1 = Constants.FOO; //直接访问
System.out.println(var1);
}
复制代码
const val
修饰版本
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String var1 = "foo"; //内联
System.out.println(var1);
}
复制代码
如果一个类只是用来装载常量,那我们可以放心大胆地“丢弃这个类和companion object”,使用Kotlin的文件级属性(顶层属性) 直接在kt文件中
const val FOO = "foo" /* 可以在此声明顶层函数,在此不做讨论 */ /* 其它类(也可以不声明,专门用这个文件存放常量) */ 复制代码
生成对应的Java代码(或许就是你在使用Java时会写上的代码)
public final class ConstantsKt {
@NotNull
public static final String FOO = "foo";
}
复制代码
在Kotlin里,你可以不带类名地使用这些顶层属性,比如 println(FOO)
,在Java中使用这些值的时候,你需要 ConstantsKt .FOO
。下面的注解可以去掉Kt后缀
@file:JvmName("Constants")
复制代码
使用注解后生成的Java代码
public final class Constants {
@NotNull
public static final String FOO = "foo";
}
复制代码