mzs
mzs

Reputation: 21

Duplicate fields in data classes that extend other (sealed) classes?

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?

Upvotes: 2

Views: 1499

Answers (3)

Eugen Pechanec
Eugen Pechanec

Reputation: 38223

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

Upvotes: 0

DVarga
DVarga

Reputation: 21799

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()

Upvotes: 2

yole
yole

Reputation: 97123

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)

Upvotes: 0

Related Questions