dskst's diary

Life and Tech Blog

Kotlin / Inline class を Java コードから学ぶ

f:id:dskst9:20190310230649p:plain

前回の記事にてKotlin data classを学びました。

dskst9.hatenablog.com

今日はInline classについて見ていきましょう。

Inline class

Inline classについて、同僚がクールな記事を書いてくれたので紹介します。
リンク先記事でもあるように、primitiveで処理されることによりパフォーマンスが飛躍的に向上します。

tech.askul.co.jp

さて、このJavaコードはどのようにコンパイルされているのでしょうか?

Kotlin to Java

では、リンク先記事のソースをJavaで見てみましょう。
@Metadataは除外して記載しています。

data class

public final class ItemPrice {
   private final int value;
   @NotNull
   private static final ItemPrice ZERO = new ItemPrice(0);
   public static final ItemPrice.Companion Companion = new ItemPrice.Companion((DefaultConstructorMarker)null);

   @NotNull
   public final ItemPrice plus(@NotNull ItemPrice itemPrice) {
      Intrinsics.checkParameterIsNotNull(itemPrice, "itemPrice");
      return new ItemPrice(itemPrice.rawValue() + this.rawValue());
   }

   public final int rawValue() {
      return this.value;
   }

   public ItemPrice(int value) {
      this.value = value;
   }

   private final int component1() {
      return this.value;
   }

   @NotNull
   public final ItemPrice copy(int value) {
      return new ItemPrice(value);
   }

   // $FF: synthetic method
   @NotNull
   public static ItemPrice copy$default(ItemPrice var0, int var1, int var2, Object var3) {
      if ((var2 & 1) != 0) {
         var1 = var0.value;
      }

      return var0.copy(var1);
   }

   @NotNull
   public String toString() {
      return "ItemPrice(value=" + this.value + ")";
   }

   public int hashCode() {
      return this.value;
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof ItemPrice) {
            ItemPrice var2 = (ItemPrice)var1;
            if (this.value == var2.value) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }

   public static final class Companion {
      @NotNull
      public final ItemPrice getZERO() {
         return ItemPrice.ZERO;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

Inline class

public final class ItemPriceInline {
   private final int value;
   private static final int ZERO = constructor-impl(0);
   public static final ItemPriceInline.Companion Companion = new ItemPriceInline.Companion((DefaultConstructorMarker)null);

   // $FF: synthetic method
   private ItemPriceInline(int value) {
      this.value = value;
   }

   public static final int plus_nnTRwYY/* $FF was: plus-nnTRwYY*/(int $this, int itemPriceInline) {
      return constructor-impl(rawValue-impl(itemPriceInline) + rawValue-impl($this));
   }

   public static final int rawValue_impl/* $FF was: rawValue-impl*/(int $this) {
      return $this;
   }

   public static int constructor_impl/* $FF was: constructor-impl*/(int value) {
      return value;
   }

   // $FF: synthetic method
   @NotNull
   public static final ItemPriceInline box_impl/* $FF was: box-impl*/(int v) {
      return new ItemPriceInline(v);
   }

   @NotNull
   public static String toString_impl/* $FF was: toString-impl*/(int var0) {
      return "ItemPriceInline(value=" + var0 + ")";
   }

   public static int hashCode_impl/* $FF was: hashCode-impl*/(int var0) {
      return var0;
   }

   public static boolean equals_impl/* $FF was: equals-impl*/(int var0, @Nullable Object var1) {
      if (var1 instanceof ItemPriceInline) {
         int var2 = ((ItemPriceInline)var1).unbox-impl();
         if (var0 == var2) {
            return true;
         }
      }

      return false;
   }

   public static final boolean equals_impl0/* $FF was: equals-impl0*/(int p1, int p2) {
      throw null;
   }

   // $FF: synthetic method
   public final int unbox_impl/* $FF was: unbox-impl*/() {
      return this.value;
   }

   public String toString() {
      return toString-impl(this.value);
   }

   public int hashCode() {
      return hashCode-impl(this.value);
   }

   public boolean equals(Object var1) {
      return equals-impl(this.value, var1);
   }

   public static final class Companion {
      public final int getZERO() {
         return ItemPriceInline.ZERO;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }

比較してみて

data classだとplusメソッドを呼び出すたびにクラスが生成されます。

   @NotNull
   public final ItemPrice plus(@NotNull ItemPrice itemPrice) {
      Intrinsics.checkParameterIsNotNull(itemPrice, "itemPrice");
      return new ItemPrice(itemPrice.rawValue() + this.rawValue());
   }

一方、inline classはplusメソッドの中はprimitive型で処理されています。

   public static final int plus_nnTRwYY/* $FF was: plus-nnTRwYY*/(int $this, int itemPriceInline) {
      return constructor-impl(rawValue-impl(itemPriceInline) + rawValue-impl($this));
   }

なるほど。これは早いわけですね。

今後が楽しみですね!みんなでKotlin愛でましょう!