我正在尝试绕过类型擦除并使用值类动态地将不同类型的值列表转换为json,并使用Json4s CustomSerializer将其返回到相应的类型。但是,我无法动态提取正确的值。由于对AnyVal的引用,我在转换为Json时被迫使用value.toString,并且当我从json重新创建字段时遇到问题。
我现在已经在斯卡拉试了三个星期了,所以我还在学习绳索。任何帮助将不胜感激。
abstract class MyVal {
def name: String
def value: AnyVal
}
trait Field {
def value: AnyVal
}
object Field {
def build(value: String): MyString = MyString(value)
def build(value: Int): MyInt = MyInt(value)
def build(value: Double): MyDouble = MyDouble(value)
}
case class MyString(val value: String) extends AnyVal with Field
case class MyInt(val value: Int) extends AnyVal with Field
case class MyDouble(val value: Double) extends AnyVal with Field
object MyVal {
case class StringVal(name: String, value: MyString) extends MyVal
case class IntVal(name: String, value: MyInt) extends MyVal
case class DoubleVal(name: String, value: MyDouble) extends MyVal
def build(name: String, value: String): StringVal = StringVal(name, MyString(value))
def build(name: String, value: Int): IntVal = IntVal(name, value)
def build(name: String, value: Double): DoubleVal = DoubleVal(name, value)
}
object Main {
def main(args: Array[String]) = {
var fields = Seq.empty[MyVal]
var row = Map("length" -> 1, "name" -> "test", "cost" -> 2.0)
var columns = Seq[String]("length", "name", "cost")
val fields: Seq[MyVal] = row foldLeft (Seq.empty[MyVal]) {
(previousFields: Seq[MyVal], currentField: Any) => {
columns map {
column => MyVal.build(column, Field.build(row(column)))
}
}
}
}
}
由于含糊不清,构建方法无法正确解析。
答案 0 :(得分:2)
Field
特征定义了我们可以为字段提供的所有可能类型:
import scala.language.existentials
import scala.language.implicitConversions
sealed trait Field extends Any {
def value: Any
}
case class MyString(val value: String) extends AnyVal with Field
case class MyInt(val value: Int) extends AnyVal with Field
case class MyDouble(val value: Double) extends AnyVal with Field
请注意下面隐含的def
。它们允许将已知类型的值转换为Field
而不显式使用build
。我们将在下面利用它。
object Field {
implicit def build(value: String): MyString = MyString(value)
implicit def build(value: Int): MyInt = MyInt(value)
implicit def build(value: Double): MyDouble = MyDouble(value)
}
对于MyVal
,我们不需要特定的子类 - 这只会复制Field
的层次结构。足以说每个值都与Field
相关联 - 这会捕获相同的信息。
sealed case class MyVal(name: String, value: Field)
我们仍然可以定义能够更轻松地构建MyVal
的暗示,尽管我们不需要这样做。
object MyVal {
// notice that we use the above Field.build.. implicits here:
implicit def build(name: String, value: String): MyVal = MyVal(name, value)
implicit def build(name: String, value: Int): MyVal = MyVal(name, value)
implicit def build(name: String, value: Double): MyVal = MyVal(name, value)
}
我不确定你要在main
函数中表达什么。下面是一个更简单的例子。这里的主要技巧是你不想拥有Map[String,Any]
。这永远不会奏效。一旦丢失了地图中的类型信息,就无法在不处理无效值的情况下构建Field
和MyVal
。您希望明确保留值只是允许值的信息,这就是Field
的用途。通过声明Map[String,Field]
并让implicit
为您做无聊的工作,您可以获得以下类型安全的代码:
object Main {
def main(args: Array[String]) = {
val row = Map[String,Field]("length" -> 1, "name" -> "test", "cost" -> 2.0)
val vals: Seq[MyVal] = (for((k, v) <- row.iterator) yield MyVal(k, v)).toSeq
}
}
特别是,您希望避免处理类型擦除。键入的代码是您的优势,类型擦除应尽可能留给运行时。