Понимание использования Scala абстрактных типов и классов - PullRequest
4 голосов
/ 06 июня 2019

Следующий код не компилируется. Почему Santorin не может съесть HorseFood? Tornado объявлен как новый Horse, а Horse является подтипом Animal, но может "съесть" HorseFood.

import scala.io.StdIn._
import util._


class Food

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

class Grass extends Food  /*extends meaning should be "more specific than*/
class Fish extends Food
class HorseFood extends Grass

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

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

class Whale extends Animal
{
    type SuitableFood = Fish
    override def eat(food: SuitableFood) = {}

}

object abstractExample1 {

  def main(args: Array[String]) {

    val bessy: Animal = new Cow
    println("bessy = " + bessy)
    bessy eat (new Grass).asInstanceOf[bessy.SuitableFood]
     /*added this line because of a great answer someone posted for this questions but he deleted*/

    val willie: Animal = new Whale
    println("willie = " + willie)

    val santorin: Animal = new Horse
    println("santorin = " + santorin)

    val tornado = new Horse
    tornado eat new HorseFood

    print("tornado = " + tornado)
    santorin.eat(new HorseFood)

  }

}

Разве это не должно быть разрешено автоматически (поскольку Horse расширяется Animal)? Почему не так?

Обратите внимание, что Tornado, как объявлено, может съесть HorseFood, который расширяет Grass, и что параметр питания в классе Horse равен Grass.

Есть ли здесь проблема с =? Я имею в виду, что SuitableFood это точно Grass, а не расширение C класса Grass.

Ответы [ 3 ]

2 голосов
/ 06 июня 2019

Санторин - это Animal, а не Horse, поэтому он "ест" тип "не HorseFood. Это потому, что ссылка на него для Animal. Изменить val santorin: Animal = new Horse до val santorin: Horse = new Horse и это будет нормально работать

1 голос
/ 06 июня 2019

Вы должны прочитать о дисперсия .

Ваши классы инвариантны.Это означает, что Animal с SuitableFood <: Food не может быть Animal с SuitableFood = HorseFood.

. Это можно увидеть, если переписать свои классы в стиле общего типа:

trait Food
class Grass extends Food
class HorseFood extends Grass

abstract class Animal[SuitableFood <: Food] {
    def eat(food: SuitableFood)
}

val animal1: Animal[HorseFood] = null
//the next line doesn't compile because Animal[HorseFood] isn't a subclass of Animal[Food]
val animal2: Animal[Food] = animal1

Итак, животное, которое может есть Food не может есть HorseFood.

1 голос
/ 06 июня 2019

Animal#SuitableFood является подтипом Food

implicitly[Animal#SuitableFood <:< Food]

Каждый экземпляр Horse, присвоенный Animal -типу val, содержит новый тип SuitableFood, который является подтипом Animal#SuitableFood

val santorin: Animal = new Horse
val santorin2: Animal = new Horse

implicitly[santorin.SuitableFood <:< Animal#SuitableFood]
implicitly[santorin2.SuitableFood <:< Animal#SuitableFood]
implicitly[santorin.SuitableFood =:= santorin2.SuitableFood] //-- fails
//                     ^^^^^^^                    ^^^^^^^
//                       these types are different
implicitly[santorin.SuitableFood =:= Grass] // -- fails

val tornado = new Horse
val tornado2 = new Horse

implicitly[tornado.SuitableFood =:= tornado2.SuitableFood] // compiles!
//                     ^^^^^^^                    ^^^^^^^
//                               Grass =:= Grass

implicitly[tornado.SuitableFood =:= Grass] // compiles

Подпись метода eat для santorin: Animal будет иметь следующий вид:

def eat(food: santorin.SuitableFood) // SuitableFood is path-dependent

santorin.SuitableFood является подтипом Food и Animal#SuitableFood

HorseFood является подтипом Food и Grass, но не подтипом santorin.SuitableFood или Animal#SuitableFood

implicitly[HorseFood <:< Food]
implicitly[HorseFood <:< Grass]
implicitly[HorseFood <:< santorin.SuitableFood]//  -- fails
implicitly[HorseFood <:< Animal#SuitableFood]//  -- fails

Вот почему вызывается метод eat на HorseFood тоже не работает.Потому что HorseFood не является подтипом santorin.SuitableFood.

И вот почему следующие работы:

santorin.eat((new HorseFood).asInstanceOf[santorin.SuitableFood])
...