# Proposal: abstract class implementing sealed interface, relaxing the rules

**URL:** <https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318>\
**Category:** Language Design\
**Created:** [July 28, 2022, 2:20pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318 "2022-07-28T14:20:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 28, 2022, 2:20pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/1 "2022-07-28T14:20:32Z")

</div>

Consider the following interfaces in a module defining a protocol:

```auto
sealed interface Animal {
    val name: String
}

interface Cat : Animal {
    fun meow()
}

interface Dog : Animal {
    fun bark()
}

```

Then one needs to implement a `Cat` and a `Dog` in a separate module, so we may have something like:

```auto
data class MyCat(override val name: String) : Cat {
    override fun meow() {
        println("$name: meow!")
    }
}

data class MyDog(override val name: String) : Dog {
    override fun bark() {
        println("$name: bark!")
    }
}

```

Did you notice? We actually want to preserve a specific format for logging: the name of the animal, followed by colon, followed by a description of the noise they make. What if we wanted to share part of the implementation?

Sure, you might say — we can use [delegated interface implementation](https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation). But it would be difficult to share state that way, so it’s not suitable for every case. And it’s more costly, as well — for it requires an additional instance to be constructed.

So, we could do the following:

```auto
abstract class MyAnimal internal constructor() : Animal {
    protected fun makeNoise(description: String) {
        println("$name: $description")
    }
}

data class MyCat(override val name: String) : MyAnimal(), Cat {
    override fun meow() {
        makeNoise(description = "meow!")
    }
}

data class MyDog(override val name: String) : MyAnimal(), Dog {
    override fun bark() {
        makeNoise(description = "bark!")
    }
}

```

The only problem is… Well, we can’t do that because the `Animal` interface is sealed and as such can’t be implemented from within another module (or even just another package). The inability of `MyAnimal` to implement `Animal` results in what I’d call our _unavoidable inconsistency of the taxonomy_.

But if Kotlin allowed sealed interfaces to be implemented by abstract classes (with an internal constructor[1]) or any sealed classes, without adding those implementations to the sealed taxonomy[2] of the interfaces, it wouldn’t be of any issue, as long as the language imposes concrete[3] implementations of those classes to implement one of the interfaces inheriting from the sealed one.  
( **NOTE:** I elaborated the necessary rules better in [this post](https://discuss.kotlinlang.org/t/abstract-class-implementing-sealed-interface/25318/2).)

# In our example,

- `MyAnimal` is allowed to implement `Animal` because `MyAnimal` is abstract;
- `MyCat` and `MyDog` are allowed to inherit from `MyAnimal` because they also implement `Cat` or `Dog`, which are subtypes in the sealed taxonomy[2] of the sealed `Animal` interface.

# In conclusion,

- literally any protocol that we wish to separate from its implementation may benefit from the proposed changes;
- the proposed changes are not breaking changes and won’t harm backwards compatibility;
- the taxonomy of sealed classes and sealed interfaces is already only enforced on compile-time, so an implementation of the changes would probably be quite straightforward, as it doesn’t affect code generation, only code validation.

**ADDENDUM:** Among the various reasons why `MyAnimal` should implement `Animal`:

- that’s logically the most sensible thing (i.e. my animals are still animals);
- we shouldn’t need workarounds (such as those suggested by various users), in order to do something that is logical, sound, and does in no way defeat the purpose of sealed interfaces;
- in [this post](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/11) I elaborated a practical example showing how necessary it can be to be able to upcast `MyAnimal` to `Animal`.

# Notes:

1. The abstract class needs to have an internal constructor or be sealed altogether, since the compiler needs to track subclasses so it can enforce the rule I specified for concrete[3] implementations. This applies recursively to any abstract subclasses and abstract subclasses of abstract subclasses.
2. By _(sealed) taxonomy_, I mean the ‘restricted class hierarchy’ of (direct or indirect) subclasses of a sealed type, as in the first paragraph [here](https://kotlinlang.org/docs/sealed-classes.html).
3. By _concrete_, I’m referring to any non-abstract class, including ones that are `open`.

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 28, 2022, 2:57pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/2 "2022-07-28T14:57:41Z")

</div>

The rules that would need to be enforced for my proposed changes to work may be formalized as follows:

- let `I` be a sealed interface defined in package `P`, in module `M`;
- let `I'` be a set of all the non-sealed interfaces inheriting, either directly or indirectly, from `I` in the sealed taxonomy of `I`;
- a class `A` defined in package `P'`, in module `M'`, where `P'` != `P` or `M'` != `M`, is allowed to implement `I` if and only if `criteria(A)` holds.

Given a class `T`, `criteria(T)` holds if and only if any of the following conditions are true:

- `T` implements at least one interface in `I'`;
- all the following conditions are true at the same time:
  - `T` is sealed;
  - for each subclass `T'` of `T`, `criteria(T')` holds;

- all the following conditions are true at the same time:
  - `T` is abstract;
  - no constructor of `T` satisfies any of the following conditions:
    - it is more visible than `internal`;
    - it is annotated with `@PublishedApi`;

  - for each subclass `T'` of `T`, `criteria(T')` holds.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [July 28, 2022, 6:33pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/3 "2022-07-28T18:33:36Z")

</div>

I probably miss something important in your post, but… doesn’t it work exactly as you said right now? `sealed` applies only to direct subtypes, so you can create abstract `MyAnimal` and extend it in other modules.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [July 28, 2022, 6:54pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/4 "2022-07-28T18:54:34Z")

</div>

Ahh, ok, I guess the thing you miss is a possibility to create subtypes that are not part of “sealed taxonomy”, so you can have `MyAnimal` and still use exhaustive `when` with only `Cat` and `Dog`. Yes, I came across the same problem once and I agree it would be helpful.

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 28, 2022, 7:28pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/5 "2022-07-28T19:28:37Z")

</div>

Yeah, l think you got me right in your [second post](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/4).

Currently, `MyAnimal` can’t implement the `Animal` interface because it’s in a different module. The key is, `MyAnimal` doesn’t need to be part of the `Animal` taxonomy, as long as [the rules I proposed](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/2) are enforced, so it would be possible to allow for such a scenario. This would be a relaxation of the current rules that apply to inheritance from sealed types.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [July 28, 2022, 7:49pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/6 "2022-07-28T19:49:06Z")

</div>

Note you can do this:

```kotlin
// module 1

interface BaseAnimal {
    val name: String
}

sealed interface Animal : BaseAnimal

// module 2
abstract class MyAnimal internal constructor() : BaseAnimal { ... }

data class MyCat(override val name: String) : MyAnimal(), Cat { ... }

```

Not ideal, but works.

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 28, 2022, 8:11pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/7 "2022-07-28T20:11:08Z")

</div>

Interesting take. I thought about doing the same already, and I’m still weighting on that for a project I’m currently working on.

Hopefully that’s gonna be helpful for some, but like you said it’s definitely far from ideal.

---

<div class="post-metadata">

**Author:** ![tlin47](https://avatars.discourse-cdn.com/v4/letter/t/bc79bd/32.png) [@tlin47](https://discuss.kotlinlang.org/u/tlin47)\
**Post date:** [July 29, 2022, 3:53am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/8 "2022-07-29T03:53:33Z")

</div>

You can use a “shadow twin” for `Animal` interface. This can be used if you don’t have access to the original module:

```auto
// module 1 unchanged from original post
sealed interface Animal {
    val name: String
}

// module 2
private interface AnimalShadow {
    val name: String
}

abstract class MyAnimal internal constructor() : AnimalShadow { ... }

data class MyCat(override val name: String) : MyAnimal(), Cat { ... }

data class MyDog(override val name: String) : MyAnimal(), Dog { ... }

```

The shadow twin (`AnimalShadow`) contains a subset of the content of the original sealed interface (`Animal`) – all parts that you need in your shared implementation (`MyAnimal`). (In this use-case `AnimalShadow` has all the content of `Animal`, because `MyAnimal` needs the `name` and `Animal` does not have any other content).

_Note: I just came up with these names (“shadow twin”). I guess these are not commonly used._

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 29, 2022, 9:46am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/9 "2022-07-29T09:46:44Z")

</div>

Thanks for your suggestion. However, this way, nothing enforces that `AnimalShadow` actually declares all the members in `Animal`, and there’s no public interface to upcast `MyAnimal` to.

---

<div class="post-metadata">

**Author:** ![tlin47](https://avatars.discourse-cdn.com/v4/letter/t/bc79bd/32.png) [@tlin47](https://discuss.kotlinlang.org/u/tlin47)\
**Post date:** [July 30, 2022, 1:11am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/10 "2022-07-30T01:11:54Z")

</div>

> [@davidecannizzo](#):
>
> Thanks for your suggestion. However, this way, nothing enforces that `AnimalShadow` actually declares all the members in `Animal`, and there’s no public interface to upcast `MyAnimal` to.

Well, I probably misunderstood your intention then.  
I thought `MyAnimal` is solely an “implementation helper” to prevent code duplication - therefore it would only be present as superclass of `MyCat` and `MyDog`, but not actually used directly as type (in a variable declaration). For that purpose, the shadow interface with minimal scope and minimal content is a good thing.

But if I understand it correctly, you instead want to use it as a type (in variable declarations?) which can be upcasted to `Animal`? Can you give a small example code snippet for that?

> [@davidecannizzo](#):
>
> However, this way, nothing enforces that `AnimalShadow` actually declares all the members in `Animal`

That’s right - although a similar comment could be made about that nothing enforces `Animal` that it does not declare additional members not present in `BaseAnimal`. The advantage of `BaseAnimal`/`Animal` is that it has less code duplication; but it requires access to module 1 to put an implementation detail helper interface into it.  
Converesly, the advantage of `Animal`/`AnimalShadow` is that it does not require access to module 1 for implementation details, but it requires to duplicate the needed signatures.

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 30, 2022, 2:15am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/11 "2022-07-30T02:15:28Z")

</div>

> [@tlin47](#):
>
> Well, I probably misunderstood your intention then.  
> I thought `MyAnimal` is solely an “implementation helper” to prevent code duplication - therefore it would only be present as superclass of `MyCat` and `MyDog`, but not actually used directly as type (in a variable declaration).

Admittedly I haven’t clearly stated my intention to have `MyAnimal` publicly exposed and implementing `Animal` so it can be used directly as a type.  
( **NOTE:** Now I edited my [original post](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318) so as to clarify this point.)

> [@tlin47](#):
>
> But if I understand it correctly, you instead want to use it as a type (in variable declarations?) which can be upcasted to `Animal`? Can you give a small example code snippet for that?

Here we go.

Let’s say my animals, being particularly picky, unlike others’ animals, got a favorite color. LOL  
So we proceed to define an additional property `favoriteColor`:

```auto
abstract class MyAnimal internal constructor() : Animal {

    abstract val favoriteColor: String

    protected fun makeNoise(description: String) {
        println("$name: $description")
    }
}

data class MyCat(
    override val name: String,
    override val favoriteColor: String
) : MyAnimal(), Cat {

    override fun meow() {
        makeNoise(description = "meow!")
    }
}

data class MyDog(
    override val name: String,
    override val favoriteColor: String
) : MyAnimal(), Dog {

    override fun bark() {
        makeNoise(description = "bark!")
    }
}

```

Then we may wish to randomly create cats and dogs of my “color-picky” variation:

```auto
fun myRandomAnimal(
    name: String,
    favoriteColor: String
): MyAnimal =
    when (Random.nextInt(0, 1)) {
        0 -> MyCat(name = name, favoriteColor = favoriteColor)
        1 -> MyDog(name = name, favoriteColor = favoriteColor)
        else -> error("Broken random implementation")
    }

```

At this point, we may want to create one of those “color-picky” animals:

```auto
fun main() {

    val myColorPickyAnimal = myRandomAnimal(
        name = "Weirdo",
        favoriteColor = "beige"
    )

    println("${myColorPickyAnimal.name}'s favorite color is ${myColorPickyAnimal.favoriteColor}")
}

```

Alright, everything feels fine… Except that it’s not. My animals are still animals, for as weird and color-picky as they may be — as such, `MyAnimal` should still implement `Animal`. A case where it’d be useful follows.

Let’s say we want to log every animal’s name, regardless of whether they’re my weirdos or not. So we got the following function:

```auto
fun logAnimal(animal: Animal) {
    println("${animal.name} is here!")
}

```

…and now we need to apply `logAnimal` to `myColorPickyAnimal`:

```auto
fun main() {

    val myColorPickyAnimal = myRandomAnimal(
        name = "Weirdo",
        favoriteColor = "beige"
    )

    logAnimal(animal = myColorPickyAnimal) // Error: `MyAnimal` is not assignable to `Animal`
    println("${myColorPickyAnimal.name}'s favorite color is ${myColorPickyAnimal.favoriteColor}")
}

```

We can’t, since `myColorPickyAnimal` is typed `MyAnimal`, which is not assignable to `Animal`.

Ultimately,

> [@tlin47](#):
>
> That’s right - although a similar comment could be made about that nothing enforces `Animal` that it does not declare additional members not present in `BaseAnimal`. The advantage of `BaseAnimal`/`Animal` is that it has less code duplication; but it requires access to module 1 to put an implementation detail helper interface into it.  
> Converesly, the advantage of `Animal`/`AnimalShadow` is that it does not require access to module 1 for implementation details, but it requires to duplicate the needed signatures.

Well, that’s right. Both [your](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/8) and @broot[’s suggestions](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/6) are valid, in different circumstances — though they don’t solve in an ideal way the issue I’m having, which definitely does apply to several scenarios, as I pointed out in my [original post](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318).

---

<div class="post-metadata">

**Author:** ![tlin47](https://avatars.discourse-cdn.com/v4/letter/t/bc79bd/32.png) [@tlin47](https://discuss.kotlinlang.org/u/tlin47)\
**Post date:** [July 31, 2022, 5:47am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/12 "2022-07-31T05:47:52Z")

</div>

You want to use the returned value of `myRandomAnimal` as both `MyAnimal` and `Animal`.

The return type of a method can be specified as an intersection type using where-conditions:

```auto
fun <A> myRandomAnimal(
    name: String,
    favoriteColor: String
): A where A : MyAnimal, A : Animal { ... }

```

This way the method’s return value is of both types. However, I haven’t yet used it myself in Kotlin, so I am not sure how calling this method works. Can this probably help for your use-case? Maybe @broot knows about how to use it!?

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 31, 2022, 10:04am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/13 "2022-07-31T10:04:32Z")

</div>

Interesting, @tlin47. Actually, the example I posted was not intended to be an exact representation of my particular use case. Your solution (well, more of a workaround) may be helpful in many circumstances, but it does not necessarily solve any situation.

- How would you call `logAnimal`? Oh, well, let’s add another generics trick on every function accepting an `Animal`. LOL.
- If only just conceptually, it’s still inconsistent that `MyAnimal` is actually not an `Animal`. I’ll refer to this as the _unavoidable inconsistency of the taxonomy_.

Of course, Kotlin is a Turing-complete language, and as such, any use case can be made to work, somehow. The thing is, whatever solution one comes up with, it’s still a workaround that doesn’t eliminate the unavoidable inconsistency of the taxonomy. [My proposal](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/2) would fix this by relaxing some rules that are currently stricter than they need to be.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [July 31, 2022, 10:26am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/14 "2022-07-31T10:26:29Z")

</div>

I don’t know what is your real case, but the last time I lacked a similar feature (actually, I think it was discussed somewhere here), it was really a simple case. Add a subtype of sealed interface that provides some additional functionality, but it isn’t just one of sealed subtypes. See this:

```kotlin
sealed interface Shape
interface Square : Shape
interface Triangle : Shape

interface ColoredShape : Shape {
    val color: String
}

object UncoloredSquare : Square
object UncoloredTriangle : Triangle

object RedSquare : Square, ColoredShape {
    override val color get() = "red"
}
object GreenTriangle : Triangle, ColoredShape {
    override val color get() = "green"
}

```

In this case `ColoredShape` is a shape with additional properties, but it doesn’t represent one of available shapes. Exhaustive `when` should be still possible with `Square` and `Triangle` only. I think this is a valid and not that rare case, but unfortunately right now it is hard to do it in Kotlin.

I don’t know if this is the same problem you have, something similar or just entirely different one.

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [July 31, 2022, 10:33am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/15 "2022-07-31T10:33:41Z")

</div>

> [@broot](#):
>
> I don’t know if this is the same problem you have, something similar or just entirely different one.

It’s actually not the same one. I probably wouldn’t even need to add any properties in my real use case, unlike you did on `ColoredShape` and I showed on `MyAnimal`.

Unfortunately, your idea comes at detriment of forcing any additional properties to be foreseen and be added to the _protocol_ module, which uselessly hinders extensibility.

Also, your way, we cannot have a shared implementation for `RedSquare` and `GreenTriangle`, which was the main point I made in [my original post](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318), where `MyCat` and `MyDog` would have `MyAnimal` as their shared implementation of the `Animal` interface.

> [@broot](#):
>
> I think this is a valid and not that rare case, but unfortunately right now it is hard to do it in Kotlin.

Nice acknowledgement! That’s where [my proposal](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/2) comes in.

---

<div class="post-metadata">

**Author:** ![guai](https://avatars.discourse-cdn.com/v4/letter/g/7ba0ec/32.png) [@guai](https://discuss.kotlinlang.org/u/guai)\
**Post date:** [August 2, 2022, 4:44pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/16 "2022-08-02T16:44:19Z")

</div>

Hi.  
What would exhaustive _when_ do when you pass this not-actually-sealed object to it?

---

<div class="post-metadata">

**Author:** ![davidecannizzo](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/davidecannizzo/32/6549_2.png) [@davidecannizzo](https://discuss.kotlinlang.org/u/davidecannizzo)\
**Post date:** [August 2, 2022, 4:47pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/17 "2022-08-02T16:47:05Z")

</div>

An object that is not actually sealed can’t exist, since objects can’t be made of non-concrete types. And my specs specify that such class would be abstract and subclasses would be required to implement a non-sealed interface that extends the sealed one.

---

<div class="post-metadata">

**Author:** ![guai](https://avatars.discourse-cdn.com/v4/letter/g/7ba0ec/32.png) [@guai](https://discuss.kotlinlang.org/u/guai)\
**Post date:** [August 2, 2022, 4:57pm UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/18 "2022-08-02T16:57:30Z")

</div>

I think I got it. looks like a nice feature to have

---

<div class="post-metadata">

**Author:** ![ixchel](https://avatars.discourse-cdn.com/v4/letter/i/4da419/32.png) [@ixchel](https://discuss.kotlinlang.org/u/ixchel)\
**Post date:** [January 17, 2023, 7:09am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/20 "2023-01-17T07:09:45Z")

</div>

I ran into this problem several times over the last few weeks. It is really quite obnoxious, and I have not found any really good workaround for the problem when it happens. I had to basically make my class non-sealed in order to work around the problem, thus requiring useless ‘else’ clauses in my ‘when’ clauses that would have been unnecessary otherwise.

My most recent case was like this:  
in package com.example.interfaces:

```auto
sealed interface S
interface S1 : S
interface S2 : S
// abstract class Abstract2 : S // see below

```

in package com.example.implementation:

```auto
abstract class Abstract : S // This currently errors out ( :-( ), but I don't think it should (see below).
class S1Impl : Abstract, S1 // Should be OK: non-abstract and inherits an S alternative (S1)
class S2Impl : Abstract, S2 // Should be OK: non-abstract and inherits an S alternative (S2)
class Nope : S // Should be an error (since Nope inherits S (but not S1 or S2) and is not abstract).
class Nope2 : AbstractS // Should also be an error (Nope2 inherits S (but not S1 or S2) and is not abstract.

```

Several things I noticed:  
#1: With Kotlin’s current design, class Abstract triggers a compiler error. However, as far as I can tell, there is really no reason to prevent an abstract class in another package from inheriting from a sealed class or interface. There is only really a reason to prevent a _non-abstract_ class from inheriting from a sealed class or interface, since if a ‘when’ clause were used among the sealed class’s alternatives, such a class would not match any branch of the ‘when’. I think that Kotlin’s current design unnecessarily constrains code that can be written, by preventing abstract subclasses of sealed types, when it is only really necessary to prevent non-abstract subclasses of sealed types.  
#2: With Kotlin’s current design, if I were to declare a class like “Abstract2” as in the above, there would be no way to prevent it from being treated as one of the valid sealed alternatives (other than to move it to another package). This is because, unlike Java, in Kotlin, a sealed class does not list its specific allowed subclasses (in Java a sealed class can have a ‘permits’ clause) and also there is no way to indicate that a subclass of a sealed class in the same package should not be considered one of the alternatives (this is done using the ‘non-sealed’ keyword in Java). Because there is no equivalent to “non-sealed” in Kotlin, any ‘when’ clause involving class S would have to have a branch for ‘Abstract2’ even though I probably only really wanted to have to have a branch for S1 and S2, and intended for any subclass of Abstract2 to inherit from S1 or S2 eventually.

So my proposals for Kotlin are:  
P1: It should be allowed for an abstract class to inherit from a sealed class in a different package.  
P2: It should be an error for a non-abstract class to inherit (directly or indirectly) from a sealed class in a different package unless the class also inherits (directly or indirectly) from one (or more?) of the sealed class’s alternatives.  
P3: Add a new keyword like Java’s “non-sealed”, such that if an abstract class in the same package as a sealed class inherits from the sealed class, and that class has the non-sealed keyword, then that class should not be considered to be one of the exhaustive alternatives for that sealed class (in a ‘when’ clause for instance). Due to P2, it will still be required that any non-abstract subclass of that abstract class will inherit one (or more?) of the sealed class’s alternatives that aren’t marked with non-sealed.

Comments? Are there any inconsistencies or holes here?

---

<div class="post-metadata">

**Author:** ![ixchel](https://avatars.discourse-cdn.com/v4/letter/i/4da419/32.png) [@ixchel](https://discuss.kotlinlang.org/u/ixchel)\
**Post date:** [January 17, 2023, 7:30am UTC](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/21 "2023-01-17T07:30:48Z")

</div>

I don’t think that what you have listed [here](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/2) is the correct criteria, for several reasons:  
#1: I don’t think that it matters whether I’ interfaces are sealed or not. If they inherit from I in the sealed taxonomy, they are potential targets for a ‘when’ clause, even if they themselves are also sealed and have their own sealed hierarchies.  
#2: I don’t think constructor visibility or @PublishedAPI should matter. What matters is “is the class abstract, or is it concrete?” If it’s abstract (and therefore not constructible other than by a subclass), then it should not be constrained to inherit one of the classes in the sealed taxonomy of I. If it’s not abstract (and therefore possible to instantiate) then if it inherits I, it MUST also inherit one of the classes in the sealed taxonomy of I, or else exhaustive ‘when’ clauses over that taxonomy will be broken.  
#3: I don’t think that requiring that subclasses of a class like T be sealed or also satisfy the criteria is necessary.  
#4: There are necessary constraints on non-abstract classes in the hierarchy that your criteria does not discuss.

See my prior post [here](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318/20) for what I think are the correct criteria, and a rationale as to why.

[Next page](https://discuss.kotlinlang.org/t/proposal-abstract-class-implementing-sealed-interface-relaxing-the-rules/25318.md?page=2)
