Наследование case to case в Scala
У меня есть абстрактный класс, который я расширяю и делаю множество классов случаев. Теперь я хочу скопировать экземпляры этих классов case, просто изменяя первый параметр, поэтому я использую метод класса case < copy
.
Так как я должен делать это для всех классов case, которые были расширены из общего абстрактного класса, а не для всех, я попытался сделать его общим и сделал абстрактным классом класс case.
Тогда Scala дает мне следующее:
класс case Осьминог имеет предки-предки-организмы, но наследование на случай болезни запрещено. Чтобы преодолеть это ограничение, используйте экстракторы для соответствия шаблону на нелистовых узлах.
Код:
abstract class Organism(legs: Int)
case class Octopus(override val legs: Int, weight: Double, ...)
case class Frog(override val legs: Int, ...)
def clone(o: Organism) = o.copy(legs = -1)
Это то, что я хочу сделать. Но если я не могу заставить метод clone
работать, тогда мне придется делать копирование как для Octopus
, так и Frog
.
Любые предложения, чтобы уменьшить эту многословие?
Ответы
Ответ 1
Вы не можете полностью абстрагироваться от методов класса case copy
. Я бы предложил использовать объективы из Shapeless или Monocle:
trait Organism { def legs: Int }
// monocle @Lenses uses a macro to generate lenses
@Lenses case class Octopus(override val legs: Int, weight: Double, ...)
extends Organism
@Lenses case class Frog(val legs: Int, ...) extends Organism
def clone[O <: Organism](o: O, legsLens: Lens[O, Int]): O =
legsLens.set(-1)(o)
val myOctopus = Octopus(8, 2.4, ...)
val myFrog = Frog(2, ...)
// use the generated Lenses
val cloneOctopus: Octopus = clone(myOctopus, Octopus.legs)
clone(myFrog, Frog.legs)
Ответ 2
Использование только стандартного scala такого обобщенного метода копирования в абстрактном (супер) классе не существует: как он узнает, как все подклассы могут быть клонированы/скопированы? Особенно, что новые подклассы могут быть добавлены в будущем.
Насколько я знаю, двумя основными подходами к реализации такого абстрактного метода являются:
1) создайте функцию, совпадающую по всем подклассам:
def clone(o: Organism) = o match {
case o: Octopus => o.copy(legs = -1)
case f: Frog => f.copy(legs = -1)
}
Затем каждый раз, когда добавляется новый подкласс, его необходимо добавить в эти функции. Это наиболее подходит для использования с закрытым абстрактным классом.
2) добавьте метод makeClone
к абстрактному API (зарезервированное имя clone
):
abstract class Organism(legs: Int){
def makeClone(legNumber: Int): Organism
}
case class Octopus(legs: Int, weight: Double) extends Organism(legs) {
def makeClone(legNumber: Int) = this.copy(legs = legNumber)
}
Обратите внимание, что хотя функция в (1) всегда возвращает Organism
, здесь метод Octopus.makeClone
возвращает Octopus
.