I want a `sealed class` where children share some ...
# getting-started
y
I want a
sealed class
where children share some common variables. how do I do this with the least amount of boilerplate? what I was recommended is something like
Copy code
sealed class Parent {
    abstract val s: String

    data class Derived1(override val s: String, ...) : Parent()
    data class Derived2(override val s: String, ...) : Parent()
    
    sealed class Derived3() : Parent() {
        data class Derived4(override val s: String, ...) : Derived3()
    }
}
which repeats the
val s
declaration
g
yes, version with abstract property is the shortest one, there is no version shorter
y
thanks.
e
there is another similar option,
Copy code
sealed class Parent(val s: String) {
    class Derived1(s: String) : Parent(s)
but it means something a bit different
y
yeah, that doesn’t disallow creating a
Parent
class, right?
g
No, it’s not data class anymore. And you still cannot create parent
y
if I declare it that way, there’s only a single copy of
s
, right?
no shadowing or anything like that?
e
if you were to write
Copy code
sealed class Parent(open val s: String) {
    class Derived(override val s: String) : Parent(s)
then you could potentially have a `data class`… but I would not recommend it. it's confusing and does lead to multiple fields internally shadowing
1
if you want
data class
then stick with what you wrote originally
y
so if I don’t actually need
data class
, the minimum boilerplate version would be the above version? (
open val
which is then passed to the
Parent
constructor)
e
I would not use the
open val
version
a closed
val
passed to the
Parent
constructor is the least boilerplate version if you only have normal
class
and
object
, yes
y
and in that case, no shadowing if I avoid
data class
, right? sorry for asking again, this is just somewhat confusing.
e
taking
sealed
out of the discussion for a moment,
Copy code
open class Parent(val s: String)
class Derived(s: String) : Parent(s)
what makes you think there's any shadowing?
y
just a coworker’s warning about how
abstract
+ re-declarations is the right way, because otherwise shadowing might happen. I realize now that it’s a
data class
thing. there’s actually no need for the sugar introduced by
data
in my current use case
e
yes, for
data class
(or any other time you want to have a concrete
val
in the child) you should prefer an
abstract val
over a concrete
open val
in the parent
y
thanks for the help.
e
if you think about what's happening under the covers of the non-recommended
Copy code
open class Parent(open val s: String)
class Derived(override val s: String) : Parent(s)
it's equivalent to the following Java
Copy code
public class Parent {
    private final String s;
    public Parent(String s) {
        this.s = s;
    }
    public String getS() {
        return this.s;
    }
}
public final class Derived {
    private final String s;
    public Derived(String s) {
        super(s);
        this.s = s;
    }
    @Override
    public String getS() {
        return this.s;
    }
}
where you have two getters and two fields. the getters aren't problematic because virtual dispatch will always pick the more specific one, but the fields
Parent.s
and
Derived.s
are definitely both present in an object of type
Derived
y
oh, that makes sense. 👍
s
in other news: if you use a sealed interface instead of a sealed class, using data class for the implementing classes is possible.
e
sealed interface { val s }
is the same as
sealed class { abstract val s }
, as far as how the property is inherited
s
yes, but sealed interfaces have the added benefit of data classes being able to implement them.
e
data classes can implement sealed and abstract classes just fine
what data classes can't do, is have a non-property parameter in their primary constructor
but that's avoided with the pattern we're talking about here
s
I stand corrected! I was rather certain that it was otherwise 😅
157 Views