画竜点睛を衝く@mapyo

日々やった事をつらつらと書くブログです

KotlinでSealed Classを継承したクラスをGsonでシリアライズする時の話

こんな感じのseald classを作る

sealed class SampleEvent(open val name: String)

class SampleEventA(
        override val name: String
): SampleEvent(name)

こんな感じでJson作る

Gson().toJson(SampleEventA("aaaaa"))

気持ちとしては以下のようなjsonが出て欲しい

{"name":"aaaa"}

でもエラーが出る

SampleEventA declares multiple JSON fields named name

ほほう

Android Studioの、

Tools > Kotlin > Show Kotlin Bytecode

でBytecodeからのJavaにDecompileしてどんな感じか見る。

public final class SampleEventA extends SampleEvent {
   @NotNull
   private final String name;

   @NotNull
   public String getName() {
      return this.name;
   }

   public SampleEventA(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super(name, (DefaultConstructorMarker)null);
      this.name = name;
   }
}

public abstract class SampleEvent {
   @NotNull
   private final String name;

   @NotNull
   public String getName() {
      return this.name;
   }

   private SampleEvent(String name) {
      this.name = name;
   }

   // $FF: synthetic method
   public SampleEvent(@NotNull String name, DefaultConstructorMarker $constructor_marker) {
      this(name);
   }
}

abstract classと継承したclassの両方でnameが作られていてそれが原因でエラーが出てるようだ。

javaでabstractを使った時にちゃんと動かすならこんな感じで書けばよさそうだった

abstract class JavaSampleEvent {
    protected final String name;

    public JavaSampleEvent(String name) {
        this.name = name;
    }
}

class JavaSampleA extends JavaSampleEvent {

    public JavaSampleA(String name) {
        super(name);
    }
}
String json = new Gson().toJson(new JavaSampleA("aaaa"));

最後に

結局、Seald Classを使わずにInterfaceを使って対応したのだけど、こういう場合はどうやってやるのがよかったのだろうか。。。??

追記

Twitterでいい感じに出来る方法を教えて頂きました!!

sealed class SampleEvent {
    abstract val name: String
}

class SampleEventA(
        override val name: String
) : SampleEvent()

abstractでプロパティを宣言すれば大丈夫だった。 sealed class使う時にいつもopen使ってやってたけど、こっちの方がよさそうだなー