问题描述
我有一个超类和一个子类,如下所示:
I have a super and a subclass as follows:
class Animal(var x: Int) {
def greeting: String = "hi im an animal"
def copy: Animal = new Animal(x)
}
class Lion(override var x: Int) extends Animal(x){
override def greeting: String = "hi im a lion"
override def copy: Lion = new Lion(x)
}
我希望它们都具有完全相同的复制功能(想象它比我给出的要大),除了返回类型之外,我希望 Lion 类在调用复制时返回一个 Lion.
I want both of them to have the exact same copy function (imagine it being larger than what I've given), except for the return type, I would like the Lion class to return a Lion when copy is invoked.
如何在不重复代码的情况下干净地覆盖 Animal 复制方法?
How can I cleanly override the Animal copy method without having code duplication?
推荐答案
原则上,方法apply
/unapply
、canEqual
/equals
/hashCode
、toString
、copy
、productArity
/productElement
/productIterator
/productPrefix
可以使用 Shapeless case classes a la carte 虽然我不确定这是否适用于类层次结构.
In principle, methods apply
/unapply
, canEqual
/equals
/hashCode
, toString
, copy
, productArity
/productElement
/productIterator
/productPrefix
can be generated with Shapeless case classes a la carte although I'm not sure whether this works with class hierarchies.
无论如何,您可以使用生成apply
宏注释
Anyway, you can generate apply
with a macro annotation
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro annotations")
class copy extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro CopyMacro.impl
}
object CopyMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
val paramNamess = paramss.map(_.map {
case q"$_ val $tname: $_ = $_" => tname
case q"$_ var $tname: $_ = $_" => tname
})
val tparamNames = tparams.map {
case q"$_ type $tpname[..$_] = $_" => tpname
}
val doesOverrideCopy = parents.map {
case q"${parent@tq"$_[..$_]"}(...$_)" => parent
case parent@tq"$_[..$_]" => parent
}.exists(tree =>
c.typecheck(tree.duplicate, mode = c.TYPEmode)
.tpe
.member(TermName("copy")) != NoSymbol
)
val copyMod = if (doesOverrideCopy) Modifiers(Flag.OVERRIDE) else NoMods
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
$copyMod def copy: $tpname[..$tparamNames] = new $tpname[..$tparamNames](...$paramNamess)
}
..$tail
"""
}
}
}
用法:
@copy
class Animal(val x: Int) {
def greeting: String = "hi im an animal"
}
@copy
class Lion(override val x: Int) extends Animal(x) {
override def greeting: String = "hi im a lion"
}
//scalac: {
// class Animal extends scala.AnyRef {
// <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// def greeting: String = "hi im an animal";
// def copy: Animal = new Animal(x)
// };
// ()
//}
//scalac: {
// class Lion extends Animal(x) {
// override <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override def greeting: String = "hi im a lion";
// override def copy: Lion = new Lion(x)
// };
// ()
//}
或者,由于 class Animal(val x: Int)
是 case-class-like 你可以尝试使用 shapeless.Generic
Alternatively, since class Animal(val x: Int)
is case-class-like you can try to use shapeless.Generic
implicit class CopyOps[A](a: A)(implicit generic: Generic[A]) {
def copy: A = generic.from(generic.to(a))
}
这篇关于如何有效/干净地覆盖复制方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!