【问题标题】:Want runtime polymorphism while using type parameter or generic?在使用类型参数或泛型时想要运行时多态性?
【发布时间】:2015-03-26 05:46:47
【问题描述】:

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

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(在最后一行)。 (即使用基本类型 Animal 键入任意子类型并运行没有问题。)


否则我可以从 Animal 中删除类型参数并在 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")
    }
  }
}

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

【问题讨论】:

    标签: scala generics polymorphism abstract-type


    【解决方案1】:

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

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

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

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

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

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

    编译?如果您认为答案是“是”,请考虑makeAnAnimal() 可以返回Cow

    【讨论】:

    • 谢谢。我知道了。通用代码是正确的。在这种情况下,它给出了正确的语义,并且比其他情况下更强大的类型。
    【解决方案2】:

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

    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"
    

    【讨论】:

      猜你喜欢
      • 2012-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 2021-05-11
      相关资源
      最近更新 更多