Шаблон фабрики 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 _ =>
}