2017-02-20 1 views
1

У меня есть основной пример из главы 20.7 «Программирование в Скале» (Martin Odersky, Lex Spoon и Bill Venners) по теме «Абстрактные типы». Код ниже в листинге 20.10, за исключением того, что я добавил две последние строки, которые кажутся якобы вытекающим из предыдущего примера:Почему лучший способ избежать попадания в ловушку от абстрактного типа 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) {} 
} 
class Fish extends Food 
val bossy: Animal = new Cow // If the compiler were helpful, this would error. 
bossy.eat(new Grass) // error! 
// type mismatch; found: Grass, required: bossy.SuitableFood 

Как я уже говорил выше, эти две линии, где властная объявлен как животное на самом деле не в примере, но, по-видимому, очень разумный концептуальный скачок. На уровне абстрактного класса Animal (объявленный тип bossy) член типа ApproFood по-прежнему является абстрактным. Таким образом, ничто не будет удовлетворять компилятору, даже если он выглядит так, как будто он хочет получить зависящий от пути тип при вызове метода.

Если я заявляю о своем Вэл быть типа коровы, работ вызова метода следующим образом:

val bessy: Cow = new Cow 
bessy.eat(new Grass) // happy-happy 

Учитывая, что нет ничего, что я мог бы поставить в методе «съесть()» призыв к властным (объявленный как Animal), чтобы удовлетворить компилятор, почему компилятор даже разрешает bossy быть объявлен как Animal /, созданный как корова? Другими словами, какое возможное использование, допускающее объявление/экземпляр объекта, но не вызов метода, имеет?

Есть ли «лучшая практика» для этой функции в Scala, учитывая, что абстрактное уточнение типа члена, по-видимому, преднамеренно разрешает что-то, что обычно запрещено в программировании OO? Возможно, кто-то нашел убийцу?

Я очень хочу видеть это поведение как нечто, что имеет смысл. Каков мотивирующий прецедент для этой функции, т. Е. Объявление абстрактного типа, а затем уточнение этого типа в производном классе, так что подтип имеет более утонченный тип, чем супертип?

+0

Хорошо. Это не ошибка компиляции. Вы могли бы по-прежнему сопоставлять совпадение с вашим боссом val, чтобы иметь возможность получить свой правильный подтип и вызвать правильный метод. – Falmarri

+1

Об этом вопросе [здесь] (http://stackoverflow.com/questions/20070998/abstract-type-in-scala), [здесь] (http: // stackoverflow.com/questions/20754143/no-dynamic-binding-when-abstract-type-involved-in-scala), [здесь] (http://stackoverflow.com/questions/32161100/scala-types-class-a-is -not-equal-to-the-t-where-t-is-type-ta) и [здесь] (http://stackoverflow.com/questions/37756383/path-dependent-types-example-doesnt-work). – jwvh

ответ

1

Учитывая, что нет ничего, что я мог бы поставить в методе «съесть()» призыв к властным (объявленный как животное), чтобы удовлетворить компилятор, почему компилятор даже позволяет властным быть объявлены как животное/созданный как корова?

  1. Существует: bossy.eat((new Grass).asInstanceOf[bossy.SuitableFood]). Конечно, это не значит, что вы должны писать такой код.

  2. Даже если бы не было, есть много вещей, которые вы можете сделать с bossy без вызова eat метод: положить его в List, получить его хэш-код, и т.д. и т.п.

0

Вы все еще можете делать другие полезные вещи. Вы можете сделать Animal съесть Food, если вы можете подтвердить, что это SuitableFood. Когда вы можете сделать бросок Animal, вы знаете, что все, что он выбрасывает, это то, что он может съесть, потому что его съели раньше. И вы знаете, что это Food, даже если вы не знаете точно, это Grass или Fish. Таким образом, вы можете делать на нем операции, которые возможны для каждого типа Food.

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

abstract class Food { def name: String } 
class Grass extends Food { def name = "Grass" } 
abstract class Animal { 
    type SuitableFood <: Food 
    def eat(food: SuitableFood): Unit 
    def throwUp: Option[SuitableFood] 
} 

class Cow extends Animal { 
    type SuitableFood = Grass 
    private[this] var digesting: List[Grass] = Nil 
    def eat(food: Grass) { 
    digesting = food :: digesting 
    } 
    def throwUp = digesting match { 
    case Nil => None 
    case food :: rest => 
     digesting = rest 
     Some(food) 
    } 
} 

def dispose(food: Food) = println(s"Disposing of some ${food.name}.") 

// Exiting paste mode, now interpreting. 


scala> val animal: Animal = { val cow = new Cow; cow.eat(new Grass); cow } 
animal: Animal = [email protected] 

scala> animal.throwUp foreach animal.eat // can eat his own vomit :s 

scala> animal.throwUp foreach dispose // I can dispose of all food 
Disposing of some Grass. 
Смежные вопросы