scalagenericspolymorphismabstract-type

Want runtime polymorphism while using type parameter or generic?


The following is a story about "animal eats food", and there is a cat eating a fish.

class Food
abstract class Animal {
  type F
  def eat(food: F)
}
class Fish extends Food
class Cat extends Animal {
  type F = Fish
  def eat(fish: F) {
    println("eat " + fish.getClass.getSimpleName)
  }
}

(new Cat).eat(new Fish) //eat Fish

val animal: Animal = new Cat
animal.eat(new Fish) //error: type mismatch

Now that I have used abstract-type member (or type-parameter) , I lose the runtime polymorphism (at last line). (that using base type Animal to typing arbitrary subtypes and run with no problem.)


Otherwise I can remove the type parameter from Animal and type checking in Cat:

abstract class Animal {
  def eat(food: Food)
}
class Cat extends Animal {
  def eat(food: Food) {
    food match {
      case fish: Fish => println("eat" + fish)
      case _ => throw new IllegalArgumentException("I only eat fish")
    }
  }
}

But I want the better typing for them .
So can I preserve runtime polymorphism while using type-parameter / generic ?


Solution

  • Now that I have used abstract-type member (or type-parameter) , I lose the runtime polymorphism (at last line). (that using base type Animal to typing arbitrary subtypes and run with no problem.)

    No, you can replace Animal by its subtypes (in context like this, not always!), but not vice versa. If you could, you would be able to use Object as well, since it's also a base type of Cat:

    val object: Object = new Cat
    object.eat(new Fish)
    

    Hopefully you see why this shouldn't compile.

    Or you could put it another way: should

    val animal: Animal = makeAnAnimal() 
    animal.eat(new Fish)
    

    compile? If you think the answer is "yes", consider that makeAnAnimal() could return a Cow.