【问题标题】:Understanding Scala use of abstract types and Classes理解 Scala 对抽象类型和类的使用
【发布时间】:2019-06-06 15:31:53
【问题描述】:

下一个代码无法编译。为什么Santorin不能吃HorseFoodTornado 被声明为新的Horse,而HorseAnimal 的子类型,但它可以“吃掉”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,并且Horse类的食物参数是Grass

= 有问题吗?我的意思是,SuitableFood 正是 Grass 而不是 Grass 的类 C 扩展。

【问题讨论】:

  • 为什么你没有声明这个? - val 龙卷风:动物 = 新马
  • 我做到了,龙卷风不能再吃 HorseFood 了。

标签: scala abstract-type


【解决方案1】:

Santorin 是 Animal 而不是 Horse,所以它“吃”“类型”而不是 HorseFood。这是因为对它的引用是针对Animal。 更改val santorin: Animal = new Horseval santorin: Horse = new Horse 它会正常工作

【讨论】:

  • 我知道代码是如何编译的。只是继承的一些行为对我来说似乎有点奇怪。
  • 如果你用逻辑术语而不是代码术语思考会更容易理解。在这里,您创建了一个新马,但告诉编译器它是一个“动物”。所以它只期望“动物”而不是“马”的行为。
  • 至于“龙卷风”,你没有明确地给它任何类型,所以 scala 隐含地提供它作为创建对象的类类型,即马。
【解决方案2】:

你应该阅读variance

你的类是不变的。这意味着AnimalSuitableFood &lt;: Food 不能是AnimalSuitableFood = HorseFood

如果你将你的类重写为类泛型风格,你可以看到它:

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

【讨论】:

  • 我不确定我是否理解。泛型类可以是变体。普通的非泛型类如何变体?
  • 这是一个大致相似的例子,便于理解。当你使用 Anymal 类型的对象时,编译器现在不知道什么是适合食物的类型。而且因为你的函数eat 不是协变的,它只能接收精确类型的参数适合食物(未知)
  • 如果你将你的代码重写为泛型,那么很明显 Animal with SimilarFood <: food can be an animal with similarfood="HorseFood</span">
【解决方案3】:

Animal#SuitableFoodFood 的子类型

implicitly[Animal#SuitableFood <:< Food]

分配给Animal-typed valHorse 的每个实例都包含全新的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])

【讨论】:

  • 你能解释一下吗,val willie:Animal = new Whale?
  • 这只是我为了查看继承行为而添加的一些代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 2018-05-03
  • 2016-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多