scala - 如何有效/干净地覆盖复制方法
问题描述
我有一个超类和一个子类,如下所示:
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。
如何在不重复代码的情况下干净地覆盖 Animal 复制方法?
解决方案
原则上,方法apply
/ unapply
, canEqual
/ equals
/ hashCode
, toString
, copy
, productArity
/ productElement
/ productIterator
/productPrefix
可以通过点菜的无形案例类生成,尽管我不确定这是否适用于类层次结构。
无论如何,您可以apply
使用宏注释生成
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
implicit class CopyOps[A](a: A)(implicit generic: Generic[A]) {
def copy: A = generic.from(generic.to(a))
}
推荐阅读
- postgresql - 使用 Docker-Compose 对 Postgres 进行身份验证时遇到问题
- python - 如何修复 PyQt5 中的输入按下检测
- swift - Swift Vapor:不等待 catchMap
- javascript - 使用 D3 将 Web Mercator 瓷砖重新投影到任意投影?
- javascript - 两种不同的日期格式
- docker - 主机上的 Docker 新用户,如何让 docker 知道?
- python - 无效的语法 (
,第 4 行)(语法错误) - php - 如何在 smarty 中使用 regex_replace?
- java - 在 Android Studio 中:如何更新列数据?
- rust - Rust 模式匹配如何确定绑定变量是引用还是值?