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

关于在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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容