【译】Kotlin自定义常量应该放在哪里

关于在Kolint中储存常量,这篇短文讲述了一些可供选择的方案,再者,提出了一些可能会吸引人去踏入的陷阱。但在此以前,让咱们先聊一聊被编译成Java后的Kotlinbash

Decompiling Kotlin

Kotlin的魅力之一就是你能很容易地将一些复杂的代码简单化,让编译器去代替你作繁杂的工做。data class就是一个很好的例子,短短一行代Kotlin码替代了数十行Java代码。ide

可是能力越大,责任越大。咱们很容易就会让Kotlin编译器产生一些原本就能够被简化(suboptimal)的字节码(bytecode),尤为是作安卓开发的。面对众多的类、方法和对象分配,须要意识到会不会出现上述状况。同时JetBrains也提供给了开发者一些集成到了AndroidStudio(固然还有IntelliJ IDEA)反编译工具(Kotlin编译成Java),帮助咱们了解和优化代码自己函数

关于decompile的连接在文末,不过多赘述工具

Constants in Kotlin

接下来说到优化

  • 静态常量(及其优化方式**@JvmField**)
  • 顶层常量

关于顶层常量,固然变量和方法均可以定义在顶层ui

Companion object

Kotlin中没有static关键字,若是你想在类中声明静态方法或属性,就要把他们放在companion object(伴生对象)中this

class Constants {
  companion object {
    val FOO = "foo"
  }
}
复制代码

当要在其它地方用到的时候,能够像在Java那样Constants.FOOidea

如今看一看反编译工具生成对应的Java代码(有简化)spa

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(),看起来很不优雅,接下来的方法能够避免这个状况code

const val

使用条件

  1. 做为顶层属性、companion object属性,或object属性
  2. 只可修饰String或原始类型
  3. 不能自定义getter

把上面的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定义常量

@JvmField

把上面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);
   }
复制代码

Top-Level

若是一个类只是用来装载常量,那咱们能够放心大胆地“丢弃这个类和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";
}
复制代码

总结

即便Kotlin中没有static关键字,咱们也能够很容易定义全局使用的常量,但同时也很容易使编译器产生没必要要的字节码。在decompiler的帮助下,咱们能更好地理解和使用Kotlin

Decompilers
原文连接

blog.egorand.me/where-do-i-…

相关文章
相关标签/搜索