You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中@JvmInline标记的value class未内联?求排查原因

Kotlin Value Class反编译后未内联的疑问

我最近使用value class开发工具以减少额外开销,但将其反编译为Java后,发现它似乎未被内联,不确定是否存在问题。操作流程是:在Android Studio中通过Tools->Kotlin->Show Kotlin Bytecode路径打开字节码界面,再点击Decompile按钮得到Java代码。

原始Kotlin类

@JvmInline
value class StringKey(override val key: String) : DefaultTypedBundle<String>,
    TypedIntent<String> {
    override fun put(bundle: Bundle, value: String) {
        bundle.putString(key, value)
    }

    override fun get(bundle: Bundle, defaultValue: String): String =
        bundle.getString(key, defaultValue)

    override fun get(bundle: Bundle): String? = bundle.getString(key)

    override fun put(intent: Intent, value: String) {
        intent.putExtra(key, value)
    }

    override fun get(intent: Intent): String? {
        return intent.getStringExtra(key)
    }

}

反编译后的Java类

@JvmInline
@Metadata(
   mv = {1, 8, 0},
   k = 1,
   d1 = {"\u0000B\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\u0018\u0002\n\u0002\b\u0006\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u0000\n\u0002\b\u0004\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\b\u0004\n\u0002\u0010\b\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0002\b\u0007\b\u0087@\u0018\u00002\b\u0012\u0004\u0012\u00020\u00020\u00012\b\u0012\u0004\u0012\u00020\u00020\u0003B\u0012\u0012\u0006\u0010\u0004\u001a\u00020\u0002ø\u0001\u0000¢\u0006\u0004\b\u0005\u0010\u0006J\u001a\u0010\t\u001a\u00020\n2\b\u0010\u000b\u001a\u0004\u0018\u00010\fHÖ\u0003¢\u0006\u0004\b\r\u0010\u000eJ\u0019\u0010\u000f\u001a\u0004\u0018\u00010\u00022\u0006\u0010\u0010\u001a\u00020\u0011H\u0016¢\u0006\u0004\b\u0012\u0010\u0013J\u0019\u0010\u000f\u001a\u0004\u0018\u00010\u00022\u0006\u0010\u0014\u001a\u00020\u0015H\u0016¢\u0006\u0004\b\u0012\u0010\u0016J\u001f\u0010\u000f\u001a\u00020\u00022\u0006\u0010\u0014\u001a\u00020\u00152\u0006\u0010\u0017\u001a\u00020\u0002H\u0016¢\u0006\u0004\b\u0012\u0010\u0018J\u0010\u0010\u0019\u001a\u00020\u001aHÖ\u0001¢\u0006\u0004\b\u001b\u0010\u001cJ\u001f\u0010\u001d\u001a\u00020\u001e2\u0006\u0010\u0010\u001a\u00020\u00112\u0006\u0010\u001f\u001a\u00020\u0002H\u0016¢\u0006\u0004\b \u0010!J\u001f\u0010\u001d\u001a\u00020\u001e2\u0006\u0010\u0014\u001a\u00020\u00152\u0006\u0010\u001f\u001a\u00020\u0002H\u0016¢\u0006\u0004\b \u0010\"J\u0010\u0010#\u001a\u00020\u0002HÖ\u0001¢\u0006\u0004\b$\u0010\u0006R\u0014\u0010\u0004\u001a\u00020\u0002X\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\b\u0088\u0001\u0004ø\u0001\u0000\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006%"},
   d2 = {"Lcom/jintin/bundle/key/StringKey;", "Lcom/jintin/bundle/key/DefaultTypedBundle;", "", "Lcom/jintin/bundle/key/TypedIntent;", "key", "constructor-impl", "(Ljava/lang/String;)Ljava/lang/String;", "getKey", "()Ljava/lang/String;", "equals", "", "other", "", "equals-impl", "(Ljava/lang/String;Ljava/lang/Object;)Z", "get", "intent", "Landroid/content/Intent;", "get-impl", "(Ljava/lang/String;Landroid/content/Intent;)Ljava/lang/String;", "bundle", "Landroid/os/Bundle;", "(Ljava/lang/String;Landroid/os/Bundle;)Ljava/lang/String;", "defaultValue", "(Ljava/lang/String;Landroid/os/Bundle;Ljava/lang/String;)Ljava/lang/String;", "hashCode", "", "hashCode-impl", "(Ljava/lang/String;)I", "put", "", "value", "put-impl", "(Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;)V", "(Ljava/lang/String;Landroid/os/Bundle;Ljava/lang/String;)V", "toString", "toString-impl", "sources for library Gradle: io.github.jintin:typed-bundle:0.3.0@aar"}
)
public final class StringKey implements DefaultTypedBundle, TypedIntent {
   @NotNull
   private final String key;

   public void put(@NotNull Bundle var1, @NotNull String var2) {
      put-impl(this.key, var1, var2);
   }

   // $FF: synthetic method
   // $FF: bridge method
   public void put(Bundle var1, Object var2) {
      this.put(var1, (String)var2);
   }

   @NotNull
   public String get(@NotNull Bundle var1, @NotNull String var2) {
      return get-impl(this.key, var1, var2);
   }

   // $FF: synthetic method
   // $FF: bridge method
   public Object get(Bundle var1, Object var2) {
      return this.get(var1, (String)var2);
   }

   @Nullable
   public String get(@NotNull Bundle var1) {
      return get-impl(this.key, var1);
   }

   // $FF: synthetic method
   // $FF: bridge method
   public Object get(Bundle var1) {
      return this.get(var1);
   }

   public void put(@NotNull Intent var1, @NotNull String var2) {
      put-impl(this.key, var1, var2);
   }

   // $FF: synthetic method
   // $FF: bridge method
   public void put(Intent var1, Object var2) {
      this.put(var1, (String)var2);
   }

   @Nullable
   public String get(@NotNull Intent var1) {
      return get-impl(this.key, var1);
   }

   // $FF: synthetic method
   // $FF: bridge method
   public Object get(Intent var1) {
      return this.get(var1);
   }

   @NotNull
   public String getKey() {
      return this.key;
   }

   // $FF: synthetic method
   private StringKey(String key) {
      Intrinsics.checkNotNullParameter(key, "key");
      super();
      this.key = key;
   }

   public static void put_impl/* $FF was: put-impl*/(String $this, @NotNull Bundle bundle, @NotNull String value) {
      Intrinsics.checkNotNullParameter(bundle, "bundle");
      Intrinsics.checkNotNullParameter(value, "value");
      bundle.putString($this, value);
   }

   @NotNull
   public static String get_impl/* $FF was: get-impl*/(String $this, @NotNull Bundle bundle, @NotNull String defaultValue) {
      Intrinsics.checkNotNullParameter(bundle, "bundle");
      Intrinsics.checkNotNullParameter(defaultValue, "defaultValue");
      String var10000 = bundle.getString($this, defaultValue);
      Intrinsics.checkNotNullExpressionValue(var10000, "bundle.getString(key, defaultValue)");
      return var10000;
   }

   @Nullable
   public static String get_impl/* $FF was: get-impl*/(String $this, @NotNull Bundle bundle) {
      Intrinsics.checkNotNullParameter(bundle, "bundle");
      return bundle.getString($this);
   }

   public static void put_impl/* $FF was: put-impl*/(String $this, @NotNull Intent intent, @NotNull String value) {
      Intrinsics.checkNotNullParameter(intent, "intent");
      Intrinsics.checkNotNullParameter(value, "value");
      intent.putExtra($this, value);
   }

   @Nullable
   public static String get_impl/* $FF was: get-impl*/(String $this, @NotNull Intent intent) {
      Intrinsics.checkNotNullParameter(intent, "intent");
      return intent.getStringExtra($this);
   }

   @NotNull
   public static String constructor_impl/* $FF was: constructor-impl*/(@NotNull String key) {
      Intrinsics.checkNotNullParameter(key, "key");
      return key;
   }

   // $FF: synthetic method
   public static final StringKey box_impl/* $FF was: box-impl*/(String v) {
      Intrinsics.checkNotNullParameter(v, "v");
      return new StringKey(v);
   }

   public static String toString_impl/* $FF was: toString-impl*/(String var0) {
      return "StringKey(key=" + var0 + ")";
   }

   public static int hashCode_impl/* $FF was: hashCode-impl*/(String var0) {
      return var0 != null ? var0.hashCode() : 0;
   }

   public static boolean equals_impl/* $FF was: equals-impl*/(String var0, Object var1) {
      if (var1 instanceof StringKey) {
         String var2 = ((StringKey)var1).unbox-impl();
         if (Intrinsics.areEqual(var0, var2)) {
            return true;
         }
      }

      return false;
   }

   public static final boolean equals_impl0/* $FF was: equals-impl0*/(String p1, String p2) {
      return Intrinsics.areEqual(p1, p2);
   }

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

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

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

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

原因说明

这种情况是正常的,不存在问题。核心原因在于你的StringKey实现了DefaultTypedBundle<String>和TypedIntent<String>这两个接口。

Kotlin的value class在大多数场景下会被编译器内联,直接使用其底层值(这里就是String)来避免对象创建开销。但当value class需要作为接口类型使用时,编译器必须生成实际的包装类(也就是你反编译看到的StringKey类),因为接口需要实例来实现多态特性。

具体来说:

  • 当你直接以StringKey类型使用这个值类时,编译器仍然会内联,不会创建包装对象;
  • 只有当你把它赋值给接口类型变量(比如val bundleKey: DefaultTypedBundle<String> = StringKey("key")),或者通过接口调用方法时,才会触发装箱操作,创建StringKey实例。

反编译出来的代码只是编译器生成的必要框架,实际运行时的开销只存在于需要装箱的场景,直接使用value class的地方依然能享受到无额外开销的优势。

内容的提问来源于stack exchange,提问作者Jintin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:57:01