Combine multiple is in when clause in Kotlin

Viewed 4777

Suppose I have the following:

fun makeSound(val animal: Animal) = when(animal) {
  is Lion -> animal.roar()
  is TRex -> animal.roar()
  is Cow -> animal.moo()
}

Usually I would simplify this by simply adding a RoaringAnimal interface and asking is RoaringAnimal. But is there another way of combining multiple is clauses into one?

4 Answers

UPDATE: the answer below was written before the question specified that roar was a method on the animal parameter. As the question now stands, the answer below will no longer work, however it still shows how multiple conditions can be combined in one line in a when statement.

You can combine them:

fun makeSound(animal: Animal) = when(animal) {
  is Lion, is TRex -> roar()
  is Cow -> moo()
}

Normally you can combine the clauses as shown in Yoni's answer.

But in the specific case that roar is defined on Lion and TRex, but not on Animal, you can't.

This is because the compiler inserts a smart cast:

is Lion -> animal.roar()

is really

is Lion -> (animal as Lion).roar()

but in is Lion, is TRex -> clause, it wouldn't know what cast to insert.

In principle the compiler could be extended to handle such cases by inserting another when:

is Lion, is TRex -> animal.roar()

would become

is Lion, is TRex -> when(animal) {
    is Lion -> animal.roar() // works as before
    is TRex -> animal.roar()
}

but I wouldn't expect this to happen

fun makeSound(val animal: Animal) = when(animal) {
  Lion, TRex -> animal.roar()
  Cow -> animal.moo()
}

You don't need 'is'. Just have it comma separator which works as OR here.

What you are doing here looks like a code smell to me. Why don't you use polymorphism for this type of problem?

interface Animal {
    fun makeSound()
}
class Lion : Animal {
    override fun makeSound() { println("roar") }
}
class Trex : Animal {
    override fun makeSound() { println("ROAAAARRRR") }
}
class Cow : Animal {
    override fun makeSound() { println("moo") }
}

fun makeSound(animal: Animal) {
    animal.makeSound()
    // due tue "late binding", this will automatically call the correct method - no need to check what kind of animal it is! 
}
Related