I was reading the section 20.7 of the book Programming in Scala and I was wondering why while this code compiles:
class Food
class Fish extends Food
class Gr
I believe bessy eat (new bessy.SuitableFood)
compiling is a bug (which was fixed in 2.11). Because another subtype of Animal
could have a SuitableFood
for which new
makes no sense, e.g. type SuitableFood = Food
or even type SuitableFood = Food with Int
(Food with Int
is a perfectly nice subtype of Food
!).
It's because bessie
is declared Animal
rather than Cow
. bessie.SuitableFood
is a "path-dependent type" (see below).
Try this:
val clarabelle: Cow = new Cow
clarabelle eat (new Grass)
This works because the compiler can deduce that clarabelle.SuitableFood = Grass
from clarabelle
's declared type.
Since bessie
is declared Animal
, not Cow
, the compiler can't safely deduce that bessie.SuitableFood = Grass
.* When you say new bessie.SuitableFood
, the compiler generates code to look at the actual bessie
object and generate a new instance of the appropriate type. bessie.SuitableFood
is a "path-dependent type": the "path" (the bessie.
part) that leads to the last identifier (SuitableFood
) is actually part of the type. This enables you to have a custom version of a type for each individual object of the same class.
*Well, actually, I think that if the compiler were a little smarter, it could deduce that bessie.SuitableFood = Grass
, since bessie
is a val
, not a var
, and therefore won't change its type. In other words, the compiler ought to know that even though bessie
is declared Animal
, she's really a Cow
. Perhaps a future version of the compiler will make use of this knowledge, and perhaps there's a good reason why that wouldn't be a good idea, which someone more expert than I will tell us. (Postscript: One just did! See Travis Brown's comment below.)
Regarding the second part of your question: it doesn't. Animal
doesn't specify that its food is Food
, but some subtype of Food
. Would the compiler accept this, code like your example would compile, and wrongly so. The compiler doesn't know that the necessary subtype is Grass
(which is why eat(new Grass)
doesn't work either), it just knows that there are some foods your cow can't eat and is cautious about it.