我正在尝试在case类中建模我的类层次结构。我感谢相关的discussion about duplication of case class properties here。
考虑下面显示的类层次结构。
trait Super {
def a:String
}
case class Child1(a:String, b:String) extends Super {
override def toString = s" a = $a, b= $b"
}
case class Child2(a:String, c:String) extends Super {
override def toString = s" a = $a, c= $c"
}
我有一个场景,我想使用a
,b
,c
等基本属性构建案例类对象,以及使用XML。我为这些案例类创建了伴随对象,如下所示。
object Child1 {
def apply(node: scala.xml.Node): Child1 = {
val a = (node \ "a").text
val b = (node \ "b").text
Child1(a, b)
}
}
object Child2 {
def apply(node: scala.xml.Node): Child2 = {
val a = (node \ "a").text
val c = (node \ "c").text
Child2(a, c)
}
}
在上面的代码中,我必须复制解析a
- (node \ "a").text
值的行。即使我将Super
转换为abstract
超类,似乎也没有办法做同样的事情。
我想知道如何做到这一点,我可以使用Java中Super
类中的抽象类和几个构造函数轻松完成。
UPDATE:scala.xml.Node类型的合格名称。
答案 0 :(得分:1)
事实上,对于案例类来说,这是不可能的。你必须定义一个帮助器来摆脱这样的重复。
Child1(Super.getAValue(node), c)
答案 1 :(得分:1)
让我们定义通用的xml提取器
import scala.xml.Node
trait Extract[L] extends (scala.xml.Node => L)
对于仅包含HList
record s的字符串的简单实现:
import shapeless._
import shapeless.labelled._
implicit object extractHNil extends Extract[HNil] {
def apply(node: Node): HNil = HNil
}
implicit def extractHCons[K <: Symbol, L <: HList]
(implicit witness: Witness.Aux[K], recur: Extract[L]) =
new Extract[FieldType[K, String] :: L] {
def apply(node: Node): ::[FieldType[K, String], L] = {
val name = witness.value.name
val value = (node \ name).text
field[K](value) :: recur(node)
}
}
现在,您可以在LabelledGeneric:
之上构建案例类提取器构建implicit def extractCase[C, L]
(implicit lgen: LabelledGeneric.Aux[C, L], extract: Extract[L]) =
new Extract[C] {
def apply(node: Node): C = lgen.from(extract(node))
}
从这一点开始,您可以为同伴添加简单的mixin:
abstract trait XmlReader[C] {
def extract: Extract[C]
def apply(node: scala.xml.Node) = extract(node)
}
将您的构建器实现为
object Child1 extends XmlReader[Child1] {
val extract: Extract[Child1] = implicitly
}
object Child2 extends XmlReader[Child2] {
val extract: Extract[Child2] = implicitly
}
现在您可以验证它:
val node = <node>
<a>1</a>
<b>2</b>
<c>3</c>
</node>
println(Child1(node)) // a = 1, b= 2
println(Child2(node)) // a = 1, c= 3
请注意,扩展此类解析器并不是一项非常艰巨的任务,几乎可以通过密封的case类系列来定义。请参阅picopickle作为使用无形
构建的通用解析器的示例答案 2 :(得分:0)
定义方法superA
做到
import scala.reflect.runtime.universe._
trait Super {
def a: String
}
trait Node {
def \(s: String): String = s
}
object Node {
// also you can move it to Super companion object
implicit class SuperMethodA(val node: Node) {
def superA = node \ "a"
}
}
case class Child1(a: String, b: String) extends Super {
override def toString = s" a = $a, b= $b"
}
object Child1{
def apply(node: Node): Child1 = {
val a = node.superA //.text
val b = (node \ "b") //.text
Child1(a, b)
}
}
case class Child2(a: String, c: String) extends Super {
override def toString = s" a = $a, c= $c"
}
object Child2{
def apply(node: Node): Child2 = {
val a = node.superA //.text
val c = (node \ "c") //.text
Child2(a, c)
}
}