I'm looking for an advise on this exercise I'm doing - specifically, what areas can be improved/shortened? I don't like that to feed the animals I have to use asInstanceOf but cannot find a better way. An idea is that I need to run this function for millions of objects so performance is important.
P.S. Would generics perform better in my case?
trait Animal {
type suitableFood
def eat(f: suitableFood)
}
trait Food
case class Grass (val color: String) extends Food
case class Fish (val size: String, val condition: String) extends Food
trait GrassT{
type suitableFood = Grass
}
trait FishT{
type suitableFood = Fish
}
class Cow extends Animal with GrassT{
def eat (f: suitableFood) = {
println("moo " + f.color)
}
}
class Sheep extends Animal with GrassT{
def eat (f: suitableFood) = {
println("baaa " + f.color)
}
}
class Cat extends Animal with FishT{
def eat (f: suitableFood) = {
println("mew " + f.size + " " + f.condition)
}
}
val cow = new Cow
val sheep = new Sheep
val whiteCat = new Cat
val blackCat = new Cat
val fish = new Fish("small", "fresh")
val grass = new Grass("green")
val cats = List(whiteCat, blackCat)
val farmAnimals = List(sheep, cow)
def feedAnimals[F <: Food](food: F, animals: List[Animal]) = {
animals.foreach(a => a.eat(food.asInstanceOf[a.suitableFood]))
}
feedAnimals(grass, farmAnimals)
feedAnimals(fish, cats)
You just need to put a stricter type restriction on feedAnimals
:
def feedAnimals[F <: Food](food: F, animals: List[Animal { type suitableFood = F }]) =
animals.foreach(_.eat(food))
Or:
def feedAnimals[F <: Food, A <: Animal { type suitableFood = F }](food: F, animals: List[A]) =
animals.foreach(_.eat(food))
Unlike the solution using asInstanceOf
, these solutions will fail at compile time (as opposed to runtime) for statements like feedAnimals(grass, cats)
, as desired.
Generics might be simpler in this case, if you prefer.