Jaxsun
Jaxsun

Reputation: 535

Is it possible to compose two sealed classes?

Image that I have two distinct Kotlin sealed classes.

sealed class Fruit {
  object Apple : Fruit()
  object Orange : Fruit()
  object Banana : Fruit()
}

sealed class Vegetables {
  object Broccoli : Vegetable()
  object Carrot : Vegetable()
  object Spinach : Vegetable()
}

Is it possible to define a type that is encompasses Fruits and Vegetables? Something like Produce = Fruit | Vegetable

So that you could write something like

fun lookAtProduce(produce: Produce) {
  when (produce) {
    is Carrot -> {
      return "Orange"
    }
    is Apple -> {
      return "Red"
    }
    ....
  }
}

fun putItInSalad(vegetable: Vegetable) {
  when (vegetable) {
    is Spinach -> {
      return true
    }
    is Carrot -> {
      return false
    }
    .....
  }
}

Upvotes: 0

Views: 726

Answers (3)

Louis Wasserman
Louis Wasserman

Reputation: 198113

You can't create a union type, but you can always emulate it with another sealed type.

sealed class Produce {
  class FruitProduce(val fruit: Fruit): Produce()
  class VegetableProduce(val vegetable: Vegetable): Produce()
}

...and write

fun color(produce: Produce): String {
  return when (produce) {
    is Fruit -> when (produce.fruit) {
      is Orange -> "Orange"
      ...
    }
    is Vegetable -> when (produce.vegetable) {
      is Broccoli -> "Green"
      ...
    }
  }
}

Nothing better than this is possible today: you will need to wrap your fruit and vegetable objects in FruitProduce and VegetableProduce.

Upvotes: 2

gidds
gidds

Reputation: 18577

I think you're asking about a union type.  There have been long discussions about adding one to Kotlin, but it doesn't look likely to happen soon.

Meanwhile, you can of course have an explicit parent class:

sealed class Produce

The rest then works pretty much as you'd expect:

sealed class Fruit : Produce() {
    object Apple : Fruit()
    object Orange : Fruit()
    object Banana : Fruit()
}

sealed class Vegetable : Produce() {
    object Broccoli : Vegetable()
    object Carrot : Vegetable()
    object Spinach : Vegetable()
}

fun lookAtProduce(produce: Produce) =
    when (produce) {
        is Vegetable.Carrot ->  "Orange"
        is Fruit.Apple ->  "Red"
        else -> TODO()
    }
}

fun putItInSalad(vegetable: Vegetable) =
    when (vegetable) {
        is Vegetable.Spinach -> true
        is Vegetable.Carrot -> false
        else -> TODO()
    }
}

(You have to qualify Carrot etc. unless you add static imports for them.)

(The nice thing about TODO() is that it works both as documentation and as a way to get incomplete code to compile!)

Of course, this requires you to have access to both the types you want to combine, so you can add the common parent class.  If you can't do that, a more long-winded option would be to use an Either class (see e.g. here).  That would need the produce to be explicitly wrapped and unwrapped, but is probably the nearest you can get in that case.

Upvotes: 4

broot
broot

Reputation: 28362

Just create a super class:

sealed class Produce
sealed class Fruit : Produce()
sealed class Vegetable : Produce()

Upvotes: 0

Related Questions