Proposal: abstract class implementing sealed interface, relaxing the rules

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 so as to clarify this point.)

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:

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:

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:

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:

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

…and now we need to apply logAnimal to myColorPickyAnimal:

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,

Well, that’s right. Both your and @broot’s suggestions 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.