Inferred sealed class type on exhaustive extension functions

To simulate abstract methods in sealed classes, it would be nice if the compiler be able to infer the type when all the possible members of a sealed class has an extension function with the same signature. Example:

sealed class Animal {
    class Dog(val bark: String): Animal()
    class Cat(val meow: String): Animal()
}

fun Animal.Dog.animalSound() {
    println("dog: $bark")
}

fun Animal.Cat.animalSound() {
    println("cat: $meow")
}

In this case I’m able to call parameter.animalSound() helping the compiler to know the type using a smart cast, like this

fun main(parameter: Animal) {
    when (parameter) {
        is Animal.Dog -> parameter.animalSound()
        is Animal.Cat -> parameter.animalSound()
    }
}

However, I was wondering if would be possible to have something like this, to extend the functionality of the sealed class in the same way that would behave an abstract method

fun main(parameter: Animal) {
    parameter.animalSound()
}

Sealed classes are implicitly abstact and can have abstact methods:

sealed class Animal { 
   abstract fun sound(): Unit
}

You don’t need to simulate anything.

Yes, I know that you can add an abstract method, I edited the question to make it clear. I don’t want to modify the original sealed class (think about it like a type without behavior), I just want to add functionality when I’m receiving the type for example in upper layers of my architecture.

Why don’t you define the extension methond on the sealed class then? Feels even clearer than relying on the existence of the “sister” functions.

1 Like

Well, if the extension is in the sealed class, you will need also an exhaustive when to know the specific type like this:

fun Animal.animalSound() {
    when (this) {
        is Animal.Dog -> println("dog: $bark")
        is Animal.Cat -> println("cat: $meow")
    }
}

The point is that when evaluation might be reduced if the compiler is able to check the exhaustive definition of the extensions (and only allow call the method if all the members of the sealed class have it)

This is a bit of a band-aid but you could get the same behavior by just doing this.

when (this) {
    is Animal.Dog -> ...
    else -> ... // you know the only option left is Animal.Cat.
}