Шаблон фабрики Scala возвращает непригодный абстрактный тип

Пожалуйста, дайте мне знать, как заставить следующий фрагмент кода работать как задумано. Проблема в том, что компилятор Scala не понимает, что моя фабрика возвращает конкретный класс, поэтому мой объект нельзя использовать позже. Могут ли помочь TypeTags или параметры типа? Или мне нужно реорганизовать код другим способом? Я (очевидно) новичок в Скале.

trait Animal
trait DomesticatedAnimal extends Animal
trait Pet extends DomesticatedAnimal {var name: String = _}
class Wolf extends Animal
class Cow extends DomesticatedAnimal
class Dog extends Pet

object Animal {
    def apply(aType: String) = {
        aType match {
            case "wolf" => new Wolf
            case "cow" => new Cow
            case "dog" => new Dog
        }
    }
}

def name(a: Pet, name: String) {
  a.name = name
  println(a +"'s name is: " + a.name)
}                                               

val d = Animal("dog")                                                     
name(d, "fred") 

Последняя строка кода терпит неудачу, потому что компилятор думает d является Animalне Dog,

3 ответа

Вы должны создавать сопутствующие объекты с apply метод для каждого подкласса Animal вместо Animal черта характера. Кроме того, использование изменяемого поля считается плохой практикой, как вы это делали с name,

Вы можете сделать это, не меняя ничего другого:

val d = Animal("dog").asInstanceOf[Dog]     //> d  : Test.Dog = Test$$anonfun$main$1$Dog$1@1030dda
name(d, "fred")                                   //> Test$$anonfun$main$1$Dog$1@1030dda's name is: fred

Но я не думаю, что это очень хорошая идея...

Я не хочу звучать грубо, но компилятор прав, предполагая, что d является Animal потому что это то, что Animal.apply метод возвращает.

Как уже указывалось, вы можете вызвать тип d с явным приведением, но это просто не будет безопасным для типа. Это позволит использовать ваши знания о реализации метода как программиста, что в конечном итоге станет источником ошибок по мере роста вашей кодовой базы, и вы, возможно, неожиданно измените предыдущий код.

Если вам нужно позвонить Pet метод, то вы бы лучше использовать фабричный метод, который создает Pet объекты, или, по крайней мере, проверить тип объекта перед выполнением приведения типа, используя

if (d.isInstanceOf[Pet]) name(d.asInstanceOf[Pet], "Fred")

Или еще лучше, используя сопоставление с образцом

val d = Animal("dog")
d match {
  case p: Pet => name(p, "fred")
  case _ =>
}
Другие вопросы по тегам