2

When a data class extends a sealed class containing a non-abstract open val property, the generated child data class contains private fields that duplicate the private fields of the parent class.

sealed class Foo(open val field1: String? = null)

data class Bar(override val field1: String? = null) : Foo(field1)

Output from javap -p Foo.class:

public abstract class com.example.Foo {
    private final java.lang.String field1;
    public java.lang.String getField1();
    private com.example.Foo(java.lang.String);
    com.example.Foo(java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
    public com.example.Foo(java.lang.String, kotlin.jvm.internal.DefaultConstructorMarker);
}

And javap -p Bar.class:

public final class com.example.Bar extends com.example.Foo {
    private final java.lang.String field1;
    public java.lang.String getField1();
    public com.example.Bar(java.lang.String);
    public com.example.Bar(java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
    public com.example.Bar();
    public final java.lang.String component1();
    public final com.example.Bar copy(java.lang.String);
    public static com.example.Bar copy$default(com.example.Bar, java.lang.String, int, java.lang.Object);
    public java.lang.String toString();
    public int hashCode();
    public boolean equals(java.lang.Object);
}

The bytecode for Bar.class contains its own private field field1; the field in the parent class does not appear to be re-used by the child class.

When using frameworks that set fields using reflection, which field will be set? Why is the field in the parent class not re-used by the child class? Is there a way to change the visibility of the field in the parent class to protected so it can be re-used by the child class?

DVarga
  • 21,311
  • 6
  • 55
  • 60
mzs
  • 21
  • 4

3 Answers3

1

In that caseBar holds the field indeed twice. Two alternatives to have a single field:

sealed class Foo(val field1: String?)
data class Bar(private val hiddenField1: String? = null) : Foo(hiddenField1)

or

sealed class Foo {
    abstract val field1: String?
}
data class Bar(override val field1: String? = null) : Foo()
DVarga
  • 21,311
  • 6
  • 55
  • 60
0

The field is not reused because you declared a separate property, which has its own backing field. If you want to reuse the field, change your code to:

sealed class Foo(val field1: String? = null)

data class Bar(field1: String? = null) : Foo(field1)
yole
  • 92,896
  • 20
  • 260
  • 197
  • 2
    This doesn't compile. A data class' primary constructor can only have val or var parameters. – marstran Aug 17 '18 at 17:16
  • `data class Bar(field1: String? = null) : Foo(field1)` does not compile. If I change it to a regular class it compiles fine: `class Bar(field1: String? = null) : Foo(field1)`, but then I lose the benefits of the data class. – mzs Aug 17 '18 at 18:09
0

When using frameworks that set fields using reflection, which field will be set?

It depends on the class you use. Foo::class.java.getDeclaredField() or Bar::class.java.getDeclaredField().

See:

Why is the field in the parent class not re-used by the child class?

Why should it? You defined a field-backed property field1 in both classes. Both fields will exist but getField1() method is overridden by child class to return child class' field.

Is there a way to change the visibility of the field in the parent class to protected so it can be re-used by the child class?

Fields of lateinit properties have the same visibility as the getters. But I'm not sure that's what you want.

How about this?

sealed class Foo {
    abstract val field1: String?
}

data class Bar(override val field1: String? = null) : Foo()

See discussion here: https://twitter.com/orangy/status/1033067930248867840

Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124