Jeff Lockhart
09/04/2023, 7:19 PMJeff Lockhart
09/04/2023, 7:19 PMcompanion object
operator fun invoke()
, but I also need to instantiate a dummy subclass implementation to overcome the sealed class being abstract:
sealed class Foo(param: Unit)
// Would be nice to avoid needing this
// class and instantiate Foo directly
private class FooImpl : Foo(Unit)
fun Foo(): Foo = FooImpl()
class MutableFoo : Foo(Unit)
However, this then breaks certain uses where you might access an object's class instance. It will be FooImpl
and not the expected Foo
. Is there a fundamental reason sealed classes must be abstract?Chris Lee
09/04/2023, 7:26 PMIs there a fundamental reason sealed classes must be abstract?They don’t have to be abstract.
sealed class BaseClass {
class Foo : BaseClass()
data object Bar : BaseClass()
}
…but they cannot be instantiated:
Cannot access '<init>': it is protected in 'BaseClass'
Sealed types cannot be instantiated
What is it you are trying to accomplish?Jeff Lockhart
09/04/2023, 7:30 PMChris Lee
09/04/2023, 7:33 PMsealed class BaseClass {
data object Foo(val x : Int) : BaseClass()
data object Bar(var something : String) : BaseClass()
}
Jeff Lockhart
09/04/2023, 7:35 PMMutableFoo
subclasses Foo
because it uses all of its immutable functionality. They're not siblings, but parent/child for a reason.Chris Lee
09/04/2023, 7:35 PMFoo
is an object (or could be a class) with mutable state.Chris Lee
09/04/2023, 7:36 PMBar
that is mutable. Foo
is immutable.Jeff Lockhart
09/04/2023, 7:36 PMthere was no MutableFoo in the exampleLook at the last line of my second post with the example.
Jeff Lockhart
09/04/2023, 7:37 PMFoo
is a class also in the example. It's not a singleton object.Chris Lee
09/04/2023, 7:38 PMFoo
directly, instead of another subtype in the sealed hierarchy?Jeff Lockhart
09/04/2023, 7:41 PMJeff Lockhart
09/04/2023, 7:41 PMChris Lee
09/04/2023, 7:43 PMChris Lee
09/04/2023, 7:47 PMopen class Foo internal constructor(x : Int)
final class MutableFoo(x : Int) : Foo(x)
Jeff Lockhart
09/04/2023, 7:47 PMArray
and MutableAray
. Another is Dictionary
and MutableDictionary
. The immutable versions can't be instantiated publicly, but are returned from and passed to many APIs. They do require internal
constructors to implement in my KMP library. The mutable versions can be instantiated publicly.Chris Lee
09/04/2023, 7:48 PMinternal
.Jeff Lockhart
09/04/2023, 7:49 PMdoes this work with preventing external subclassing?While it may prevent subclassing externally, it still breaks this new expect/actual KMP requirement, as common and platform source sets are compiled separately. So there's no way for the compiler to know you didn't subclass in common or use any of the same additional API properties or method as were added in the platform implementation.
Jeff Lockhart
09/04/2023, 7:50 PMright. so on the superclass that you don’t wan to be publicly instantiable, make the constructorThis is what I'm currently doing, but can no longer do with KMP expect/actual in Kotlin 1.9.20..internal
Jeff Lockhart
09/04/2023, 7:58 PM// common
expect sealed class Foo
expect class MutableFoo() : Foo
// platform
actual sealed class Foo(
internal open val impl: FooPlatformImpl
)
// Would be nice to avoid needing this
// class and instantiate Foo directly
private class FooImpl(
impl: FooPlatformImpl = FooPlatformImpl()
) : Foo(impl)
internal fun Foo(): Foo = FooImpl()
actual class MutableFoo(
override val impl: MutableFooPlatformImpl
) : Foo(impl) {
actual constructor() : this(MutableFooPlatformImpl())
}
Jeff Lockhart
09/04/2023, 11:48 PM