我正在阅读《Scala 编程》一书的第 20.7 节,我想知道为什么这段代码编译时:
class Food
class Fish extends Food
class Grass extends Food
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: Grass) {}
}
val bessy: Animal = new Cow
bessy eat (new bessy.SuitableFood)
此代码没有(其余代码与之前相同,仅最后一行发生变化):
bessy eat (new Grass)
据我了解,Grass 的类型与 Cow.SuitableFood 相同。
另外,我对这个例子还有另一个问题:
如果 bessy 是 Animal 类型,编译器如何知道它需要合适的食物类型 -> Grass 而不是 Food 类型?因为尝试提供新的 Food 会出现类型不匹配的编译错误,但 Animal 类需要 Food 类型,并且 bessy 的类型已明确定义:Animal
最佳答案
这是因为 bessie
被声明为 Animal
而不是 Cow
。 bessie.SuitableFood
是“路径相关类型”(见下文)。
试试这个:
val clarabelle: Cow = new Cow
clarabelle eat (new Grass)
这是有效的,因为编译器可以从 clarabelle
的声明类型推断出 clarabelle.SuitableFood = Grass
。
由于 bessie
被声明为 Animal
,而不是 Cow
,编译器无法安全地推断出 bessie.SuitableFood = Grass
.* 当您说 new bessie.SuitableFood
时,编译器会生成代码来查看实际的 bessie
对象并生成适当类型的新实例。 bessie.SuitableFood
是“路径相关类型”:“path ”(bessie.
部分),它指向最后一个标识符 (SuitableFood
) 实际上是类型的一部分。这使您能够为同一类的每个单独对象拥有类型的自定义版本。
*嗯,实际上,我认为如果编译器更聪明一点,它可以推断出 bessie.SuitableFood = Grass
,因为 bessie
是一个val
,而不是 var
,因此不会更改其类型。换句话说,编译器应该知道,尽管 bessie
被声明为 Animal
,但她实际上是一头 Cow
。也许编译器的 future 版本将利用这些知识,也许有一个很好的理由为什么这不是一个好主意,比我更专家的人会告诉我们。 (后记:有人刚刚做到了!请参阅下面特拉维斯·布朗的评论。)
关于Scala 类型 : Class A is not equal to the T where T is: type T = A,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32161100/