5

I am just going through abstract type in Scala and I got an error

The example I was trying:

scala> class Food
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
defined class Food
defined class Animal

scala> class Grass extends Food
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: Grass) {}
}
defined class Grass
defined class Cow

scala> class Fish extends Food
defined class Fish

scala> val bessy: Animal = new Cow
bessy: Animal = Cow@5c404da8

scala> bessy.eat(new bessy.SuitableFood)
<console>:13: error: class type required but bessy.SuitableFood found
              bessy.eat(new bessy.SuitableFood)
                                  ^

scala> bessy.eat(bessy.SuitableFood)
<console>:13: error: value SuitableFood is not a member of Animal
              bessy.eat(bessy.SuitableFood)

scala> bessy.eat(new Grass)
<console>:13: error: type mismatch;
 found   : Grass
 required: bessy.SuitableFood
              bessy.eat(new Grass)

What are these errors?

Why can't I pass new Grass to the eat method as an argument, and when I create an object like

scala> val c=new Cow
c: Cow = Cow@645dd660


scala> c.eat(new Grass)

Could you give me some idea about this?

Roland Ewald
  • 4,490
  • 3
  • 32
  • 44
Rahul Kulhari
  • 1,045
  • 1
  • 13
  • 42

1 Answers1

5

When you assign bessy, you upcast the Cow instance to an Anmial:

val bessy: Animal = new Cow

So from a static point of view, bessy is an Animal and therefore bessy.SuitableFood abstract. Now to the errors:

  1. You cannot create an object of an abstract type with new.
  2. bessy.SuitableFood tries to access the value-member SuitableFood (i.e. def/val)
  3. Since bessy is "only" an Animal, you don't know (statically) if it can eat Grass.

What you can do, is add a method to Animal that allows you to create food:

abstract class Animal {
  type SuitableFood <: Food
  def eat(food: SuitableFood)
  def makeFood(): SuitableFood
}

And implement:

class Cow extends Animal {
  type SuitableFood = Grass
  override def eat(food: Grass) {}
  override def makeFood() = new Grass()
}

Now you may call (on any Animal):

bessy.eat(bessy.makeFood())
gzm0
  • 14,247
  • 1
  • 33
  • 57
  • +1 I had almost exactly same answer. Deleting since you were first. – drstevens Mar 04 '14 at 17:14
  • Sorry, still don't understand. Cow's SuitableFood is overrided to Grass, so new bessy.SuitableFood is new Grass. It is the the same as what you written in makeFood. The only difference is you wrapped it in a method...So why can't bessy.eat(new bessy.SuitableFood) be compiled – HalfLegend Aug 06 '18 at 09:11
  • 1
    Because the target class of a `new` call has to be statically known at compile time. By putting the `new` call into the scope of `Cow`, we achieve exactly that. – gzm0 Aug 06 '18 at 16:29