我有一个Any
数组(在现实生活中,它是Spark Row
,但足以隔离问题)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
我想对其元素进行一些操作。
因此,我已经定义了一个简单的ADT来定义类型compute
A
操作
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
鉴于我有所有操作的列表,并且知道要应用于每个元素的操作,让我们使用这些操作。
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
根据设计,对于给定的op,类型在cast
和combine
之间对齐。但是很遗憾,以下代码无法编译。错误是
Type mismatch, expected: _$1, actual: AnyVal
有办法使它工作吗?
我通过使用抽象类型成员而不是类型参数找到了解决方法。
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
有更好的方法吗?
答案 0 :(得分:2)
似乎可以通过扩展Op[A]
扩展Any => A
来简化代码:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
由于无论如何asInstanceOf
到处都是,因此它不会使代码的安全性比以前差。
更新
如果您无法更改Op
界面,则调用cast
和compute
会比较麻烦,但仍然可以:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
请注意在模式中带有类型参数的ops(i) match { case op: Opt[t] => ... }
部分:这使我们可以确保cast
返回t
接受的compute
。 / p>
答案 1 :(得分:1)
作为与Andrey Tyukin相比更通用的解决方案,您可以在Op
外部定义方法,因此即使无法修改Op
,该方法也可以使用:
def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))
buffer(0) = apply(ops(0), buffer(0))