2015-08-22 75 views
5

我正在读Scala中的书编程的部分20.7,我想知道为什么尽管该代码编译:Scala的类型:A类不等于,其中T为T:类型T = A

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) 

而且据我了解草的类型是一样的Cow.SuitableFood的。

另外,我有一个关于这个例子另一个问题:

如果BESSY的类型是动物,怎么能编译器知道它需要一个类型SuitableFood - >草,而不是一个类型的食物? “因为试图提供一种新的食物给我的类型不匹配的编译错误,但是类动物需要食品和BESSY的类型是明确定义:动物

+0

建议:将标记_path-dependent-type_添加到此问题。这可能会吸引更多地了解这种困难的人的答案。 (我仍然在努力与路径依赖类型本身。) –

+0

@ BenKovitz补充,谢谢。 – vicaba

回答

10

这是因为bessie声明Animal而非Cowbessie.SuitableFood是一个“路径依赖类型”(见下文)。

试试这个:

val clarabelle: Cow = new Cow 

clarabelle eat (new Grass) 

这工作,因为编译器可以推断clarabelle.SuitableFood = Grassclarabelle的声明的类型。

由于bessie声明Animal,不Cow,编译器不能安全地推断,bessie.SuitableFood = Grass *当你说new bessie.SuitableFood,编译器生成的代码来看看实际bessie对象,并生成相应类型的新实例。 bessie.SuitableFood是一个“路径依赖类型”:导致最后一个标识符(SuitableFood)的“path”(bessie.部分)实际上是该类型的一部分。这使您可以为同一个类的每个单独对象创建一个类型的自定义版本。


* 嗯,其实,我认为,如果编译器是一个有点小聪明,它可以推断bessie.SuitableFood = Grass,因为bessieval,不是var,因此不会改变它的类型。换句话说,编译器应该知道,即使bessie被声明为Animal,她确实是Cow。也许未来版本的编译器会利用这些知识,也许这是一个很好的理由,为什么这不是一个好主意,哪个人比我会告诉我们的更专家。 (后记:刚刚做了!见下面的Travis Brown的评论。)

+7

关于你的脚注:如果你在类型上加了一个类型注解,编译器会将它视为该类型,即使它能够推断出更具体的内容。如果你想跟踪类型成员而不是子类型,你需要使用'val bessy:Animal {type SuitableFood = Grass}'类型的细化。 –

1

关于你的问题的第二部分:它不。 Animal并未指定其食物为Food,但某些亚型为Food。编译器是否会接受这一点,像你的例子那样的代码会编译,错误的是。编译器不知道必要的子类型是Grass(这也是为什么eat(new Grass)不起作用的原因),它只知道你的母牛不能吃一些食物,并对此谨慎。

1

我相信bessy eat (new bessy.SuitableFood)编译是一个错误(在2.11中修复)。因为Animal的另一个亚型可能具有SuitableFood,其中new是没有意义的,例如, type SuitableFood = Food或甚至type SuitableFood = Food with IntFood with IntFood的完美的子类型!)。

相关问题