【譯】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-…

相關文章
相關標籤/搜索