了解 Scala 对抽象类型的使用 类

Understanding Scala use of abstract types and Classes

下一个代码无法编译。为什么Santorin不能吃HorseFoodTornado 被声明为新的 HorseHorseAnimal 的子类型,但它可以 "eat" HorseFood.

import scala.io.StdIn._
import util._


class Food

abstract class Animal
{
    type SuitableFood <: Food
    def eat(food: SuitableFood)
}

class Grass extends Food  /*extends meaning should be "more specific than*/
class Fish extends Food
class HorseFood extends Grass

class Cow extends Animal
{
    type SuitableFood = Grass
    override def eat(food: SuitableFood) = {}
}

class Horse extends Animal
{
  type SuitableFood = Grass
  override def eat(food: SuitableFood) = {}
}

class Whale extends Animal
{
    type SuitableFood = Fish
    override def eat(food: SuitableFood) = {}

}

object abstractExample1 {

  def main(args: Array[String]) {

    val bessy: Animal = new Cow
    println("bessy = " + bessy)
    bessy eat (new Grass).asInstanceOf[bessy.SuitableFood]
     /*added this line because of a great answer someone posted for this questions but he deleted*/

    val willie: Animal = new Whale
    println("willie = " + willie)

    val santorin: Animal = new Horse
    println("santorin = " + santorin)

    val tornado = new Horse
    tornado eat new HorseFood

    print("tornado = " + tornado)
    santorin.eat(new HorseFood)

  }

}

这难道不应该被自动允许吗(因为 Horse 扩展了 Animal)?为什么不是?

请注意 Tornado,正如所声明的那样,可以吃 HorseFood 扩展 Grass,并且 class Horse 处的食物参数是 Grass.

= 有问题吗?我的意思是,SuitableFood 正好是 Grass 而不是 Grass.

的 class C 扩展

Animal#SuitableFoodFood

的子类型
implicitly[Animal#SuitableFood <:< Food]

分配给 Animal 类型 val 的每个 Horse 实例都包含全新的 SuitableFood 类型,它是 Animal#SuitableFood

的子类型
val santorin: Animal = new Horse
val santorin2: Animal = new Horse

implicitly[santorin.SuitableFood <:< Animal#SuitableFood]
implicitly[santorin2.SuitableFood <:< Animal#SuitableFood]
implicitly[santorin.SuitableFood =:= santorin2.SuitableFood] //-- fails
//                     ^^^^^^^                    ^^^^^^^
//                       these types are different
implicitly[santorin.SuitableFood =:= Grass] // -- fails

val tornado = new Horse
val tornado2 = new Horse

implicitly[tornado.SuitableFood =:= tornado2.SuitableFood] // compiles!
//                     ^^^^^^^                    ^^^^^^^
//                               Grass =:= Grass

implicitly[tornado.SuitableFood =:= Grass] // compiles

santorin: Animaleat 方法签名如下:

def eat(food: santorin.SuitableFood) // SuitableFood is path-dependent

santorin.SuitableFoodFoodAnimal#SuitableFood

的子类型

HorseFoodFoodGrass 的子类型,但不是 santorin.SuitableFoodAnimal#SuitableFood

的子类型
implicitly[HorseFood <:< Food]
implicitly[HorseFood <:< Grass]
implicitly[HorseFood <:< santorin.SuitableFood]//  -- fails
implicitly[HorseFood <:< Animal#SuitableFood]//  -- fails

这就是在 HorseFood 上调用方法 eat 也失败的原因。因为 HorseFood 不是 santorin.SuitableFood.

的子类型

这就是以下工作的原因:

santorin.eat((new HorseFood).asInstanceOf[santorin.SuitableFood])

您应该阅读有关 variance 的内容。

你的类是不变的。这意味着 Animal with SuitableFood <: Food 不能是 Animal with SuitableFood = HorseFood.

把你的类改写成generic-like风格就可以看到了:

trait Food
class Grass extends Food
class HorseFood extends Grass

abstract class Animal[SuitableFood <: Food] {
    def eat(food: SuitableFood)
}

val animal1: Animal[HorseFood] = null
//the next line doesn't compile because Animal[HorseFood] isn't a subclass of Animal[Food]
val animal2: Animal[Food] = animal1

所以能吃的动物Food不能吃HorseFood

Santorin 是 Animal 而非 Horse,因此它 'eats' 'type' 而非 HorseFood。这是因为对它的引用是为了 Animal。 更改 val santorin: Animal = new Horseval santorin: Horse = new Horse 它会很好