美文网首页
【译】Kotlin自定义常量应该放在哪里

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

作者: NOSAE_b97b | 来源:发表于2019-08-28 16:55 被阅读0次

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

    Decompiling Kotlin

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

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

    关于decompile的链接在文末,不过多赘述

    Constants in Kotlin

    接下来讲到

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

    关于顶层常量,当然变量和方法都可以定义在顶层

    Companion object

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

    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
    原文链接

    https://blog.egorand.me/where-do-i-put-my-constants-in-kotlin/

    相关文章

      网友评论

          本文标题:【译】Kotlin自定义常量应该放在哪里

          本文链接:https://www.haomeiwen.com/subject/bvtmectx.html