我将以下代码作为scala脚本运行,然后获得了编译器 错误
class Food
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
class Grass extends Food
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: Grass) {}
}
val bessy:Animal = new Cow
bessy eat (new Grass)
然后我得到了一个编译器错误
script.scala:14: error: type mismatch;
found : this.Grass
required: this.bessy.SuitableFood
bessy eat (new Grass)
^
接下来,我更改了一些代码如下:
val bessy = new Cow
class Fish extends Food
bessy eat (new Fish)
我收到了另一个错误
script.scala:18: error: type mismatch;
found : this.Fish
required: this.Grass
bessy eat (new Fish)
^
两种情况下所需的类型不同,如何解释?
答案 0 :(得分:2)
我无法解释这些编译器错误的确切机制,但我确实相信这些问题需要使用类型参数化。 我可以用这种方式建模的最佳方式是:或多或少干净的方式:
object Amimals {
def main(args:Array[String]):Unit = {
trait Food
trait Animal[AllowedFood<:Food]{
def eat(food:AllowedFood)
}
class Grass extends Food
class Meat extends Food
class Herbivore[AllowedFood<:Grass] extends Animal[AllowedFood] {
override def eat(food:AllowedFood) = {println("I can eat only grass")}
}
class Cow extends Herbivore[Grass]
val bessy = new Cow
bessy eat new Grass
//bessy eat new Meat
}
}
通过这种方式,您可以实例化Cow
实例,并且它将知道它只能是Grass
或Grass
衍生物。
如果您尝试取消注释最后一行,则会产生编译错误:
Error:(22, 15) type mismatch;
found : Meat
required: Grass
bessy eat new Meat
^
答案 1 :(得分:2)
<强> TL;当您向上转移到Animal
时,你会删除1>}的知识编译器<{1}}
为了解释的目的,我冒昧地略微修改了bessy.SuitableFood = Grass
的定义:
Cow
如果您专门使用class Cow extends Animal with Food {
type SuitableFood = Grass
def moo() = "moo"
override def eat(food: SuitableFood) = println(moo())
}
,那么Cow
是什么就没问题,因此编译器可以为您提供相当有用的错误消息。
SuitableFood
请注意,类型推断为您提供了最准确的信息。
当您向上转换为父类型(val bessy = new Cow // same as val bessy: Cow = new Cow
bessy.eat(new Grass) // <- totally valid
println(bessy.moo()) // <- and again
bessy.eat(new Fish) // <- does not typecheck
)时,编译器将丢失Animal
为bessy
的知识,Cow
的知识也是如此是bessy.SuitableFood
。它只能告诉你它所知道的,所以错误信息是不同的
Grass
实际上,此时你根本无法调用val bessyAsAnimal: Animal = bessy
//bessyAsAnimal.eat(new Grass) // <- won't typecheck, your problem
(没有我们不考虑的类型转换)。让我们来看看保留这种能力的方法之一:细化类型:
bessyAsAnimal.eat(...)
这有点麻烦,但有效。我们不知道bessyRefinedAnimal是val bessyRefinedAnimal: Animal { type SuitableFood = Grass } = bessy
bessyRefinedAnimal.eat(new Grass) // <- valid again
//bessyRefinedAnimal.moo() // <- won't compile
,因此我们不能再调用Cow
- 具体方法了。不过,您可以提供它,它只会接受Cow
因此,如果您只知道Grass
,则无法提供它。试试这个:
Animal
那么,这一切有什么意义呢?那么,你仍然能够在不知道它喜欢哪种特定食物的情况下喂养动物,无论是def doSomethingWithAnimal(animal: Animal) = {
// without casts, no call to animal.feed(...) will compile
// tho if Animal has methods not dependent on SuitableFood,
// those could be used
}
还是Grass
,但知道它是正确< / strong>一:
Fish
我们将能够从具体类型知识仍然存在的地方使用这种方法:
def feedAnimal(animal: Animal)(food: animal.SuitableFood) = {
println("It's dinner time!")
animal.eat(food)
// animal.eat(new Grass) will not work
// nor will animal.moo()
// we can only use argument here
println("Feeding routine complete")
}