在Kotlin中保持常数

本文探讨了如何在Kotlin中正确使用常量,避免不必要的对象创建和方法调用,从而提高应用程序性能。通过对比不同定义方式的常量在Java字节码中的表现,文章详细解释了companion object的使用、const关键字的作用以及@JvmField注解的重要性。

解压Kotlin
如果您已经使用Kotlin一段时间,您应该注意到该语言是一个精彩的模板缩小工具。Kotlin可以轻松地用简单的代码表达复杂的东西,编译器做肮脏的工作。一个很好的例子是data class功能,它允许您轻松地用几行Kotlin替换几行Java代码。但是,众所周知,用大权力来承担很大的责任。使Kotlin编译器不会产生次优字节码并不困难,特别是如果您在使用Kotlin for Android时,必须注意代码将产生的类,方法和对象分配数。幸运的是,JetBrains有一个集成到Android Studio(和IntelliJ IDEA当然)中的反编译工具,帮助检查字节码,甚至生成类似的Java代码。后者可以轻松优化Kotlin。

有一些伟大的资源在网上关于反编译Kotlin的主题,所以我不会在这里详细介绍,只会分享一些链接:

“在科特林零样板团”由彼得·Ślesarew提供了有关如何使用反编译工具的详细说明
“Kotlin:Uncovered”,Droidcon波士顿谈话和维多利亚·贡达(Victor Gonda)系列文章都详细介绍了Kotlin的反编译
克里斯托夫B. “探索科腾的隐藏成本”系列是与科特林合作时要注意的事情的一个很好的汇编
我们现在直接跳到主要话题:常数。

科特林常数
伴侣对象
static科特林没有关键字。如果你想静态访问你的课堂的某些领域或方法,你应该把它们放在一个companion object。一个天真的方式来声明一个常量会是这样的:

class Constants {
companion object {
val FOO = “foo”
}
}
该领域将在全球范围内通过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();
  }

}
}
重要的是要注意,一个配对对象是一个实际的对象:Kotlin的Constants.FOO调用将转换为Java Constants.Companion.getFOO()。这个版本是非常糟糕的,因为它引入了一个可以避免的对象和方法。

常数
改进我们的例子的一个简单方法是标记FOO为const:

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();
  }

}
}
吸气剂消失了,我们实际上现在有直接静态访问现场,但我们仍然有一个无用的编译器生成的伴侣对象。另外需要注意的const是,它只适用于原语和字符串:

class Constants {
companion object {
// won’t compile
const val FOO = Foo()
}
}
解决方法是使用@JvmField注释val:

class Constants {
companion object {
@JvmField val FOO = Foo()
}
}
这将使Foo实例public static final。编译器内联的访问行为const和@JvmField访问之间存在重要的区别const val,@JvmField而不需要。我们来看下面的代码:

fun main(args: Array) {
println(Constants.FOO)
}
这是我们得到的@JvmField val FOO = Foo():

public final class MainKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, “args”);
Foo var1 = Constants.FOO;
System.out.println(var1);
}
}
并与const val FOO = “foo”:

public final class MainKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, “args”);
String var1 = “foo”;
System.out.println(var1);
}
}
Constants.FOO在第二个示例中没有调用,值已被内联。

删除类和对象
如果我们需要的只是一组常量 - 我们可以安全地放弃类和对象,并使用顶级的vals:

const val FOO = “foo”
结果与您在Java中编写的一样:

public final class ConstantsKt {
@NotNull
public static final String FOO = “foo”;
}
在Kotlin,您将以全球名称访问该值。如果您使用Java代码中的值,则可以调用该值ConstantsKt.FOO。为了避免Kt类名上的后缀,请使用file:@JvmName文件顶部的注释来指定更易读的名称:

@file:JvmName(“Constants”)
编译器将为类名使用提供的值:

public final class Constants {
@NotNull
public static final String FOO = “foo”;
}
结论
即使staticKotlin中没有关键字,很容易定义全局可访问的常量。很容易得到这个错误,并将冗余方法和对象分配引入到字节码中。反编译器工具可以帮助您找到并修复这种问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值