希望在使用类型参数或泛型时实现运行时多态性



下面是一个关于"动物吃食物"的故事,有一只猫吃鱼。

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

现在我已经使用了抽象类型成员(或类型参数(,我丢失runtime polymorphism(最后一行(。(使用基本类型动物键入任意子类型并运行没有问题。


否则,我可以从 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")
    }
  }
}

但是我希望为他们提供更好的打字.
那么我可以在使用类型参数/泛型时保留运行时多态性吗?

现在我已经使用了抽象类型成员(或类型参数(,我失去了运行时多态性(在最后一行(。(使用基本类型动物键入任意子类型并运行没有问题。

不,您可以将Animal替换为其子类型(在这种情况下,并非总是如此!(,但反之亦然。如果可以的话,您也可以使用Object,因为它也是Cat的基本类型:

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

希望您明白为什么这不应该编译。

或者你可以换一种说法:应该

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

编译?如果您认为答案是"是",请考虑makeAnAnimal()可能会返回Cow

我不确定这是否是最好的方法,但它似乎有效。您可以允许将任何种类的食物传递给动物,冒着类投异常的风险,并在发生时进行处理。

class Food
abstract class Animal {
  type SuitableFood <: Food
  def eat(food: SuitableFood)
  def eatWithExceptionHandling(food: Food) {
    try {
      eat(food.asInstanceOf[SuitableFood])
    }
    catch {
      case ex: ClassCastException => println("Wrong food")
    }
  }
}
class Grass extends Food
class Fish extends Food
class Cow extends Animal {
  type SuitableFood = Grass
  override def eat(food: Grass) {
    println("Cow eating grass")
  }
}
class Cat extends Animal {
  type SuitableFood = Fish
  override def eat(food: Fish) {
    println("Cat eating fish")
  }
}
val (cow, cat, grass ,fish) = (new Cow, new Cat, new Grass, new Fish)
cow.eat(grass) //"Cow eating grass"
cat.eat(fish) //"Cat eating fish"
//cow.eat(fish) // error: type mismatch
cow.eatWithExceptionHandling(grass) //"Cow eating grass"
cow.eatWithExceptionHandling(fish) //"Wrong food"

最新更新