我有一个异构类型的容器
trait Elem
trait Obj {
type E <: Elem
def elem: E
}
trait Foo extends Elem
不应该有Obj
的任何子类。我正在寻找建议,如何优雅地将Obj
的实例与多种元素类型进行匹配,以获得精确的Obj
。
例如:
def withFooObj(obj: Obj { type E = Foo }) = ()
def test(obj: Obj): Unit = obj.elem match {
case f: Foo =>
withFooObj(obj.asInstanceOf[Obj { type E = Foo }]) // !
case _ =>
}
摆脱演员会有什么优雅的想法?理想情况下,它可以作为模式匹配。
例如,我可以创建单独的提取器:
object IsFoo {
def unapply(obj: Obj): Option[Obj { type E = Foo }] =
if (obj.elem.isInstanceOf[Foo]) Some(obj.asInstanceOf[Obj { type E = Foo }])
else None
}
def test(obj: Obj): Unit = obj match {
case IsFoo(x) => withFooObj(x)
case _ =>
}
但是我有很多Elem
的子类型,并且更喜欢使用通用提取器。
编辑:由于这似乎很难,我想添加另一个放宽:允许更改Obj
以包含类型参数而不是类型成员(如果它有助于)。 不允许Obj
。
答案 0 :(得分:1)
我希望这是不可能的。考虑(假设class Foo extends Elem
)
val obj1 = new Obj {
type E = Elem
def elem = new Foo
}
由于obj.elem
与f: Foo
匹配,但obj1
实际上没有Obj { type E = Foo }
类型,因此您必须进行投射。 在这种情况下,此演员实际上是安全的,但仅仅因为实际上,这是不安全的,因为第一次调用Obj
没有例如任何 E
的方法。elem
的值有类Foo
并不意味着对elem
的所有电话都会返回Foo
。
编辑:如果您只是将演员隐藏在提取器中,那么您可以做以下事情:
case class IsElem[T <: Elem]()(implicit ct: ClassTag[T]) {
def unapply(obj: Obj): Option[Obj { type E = T }] =
if (ct.runtimeClass.isInstance(obj.elem)) Some(obj.asInstanceOf[Obj { type E = T }])
else None
}
val IsFoo = IsElem[Foo]
val IsBar = IsElem[Bar]
...
EDIT2:使用类型参数,你可以
(obj, obj.elem) match {
case (obj: Obj[Foo] @unchecked, f: Foo) => ...
case (obj: Obj[Bar] @unchecked, f: Bar) => ...
}
显然,这对安全问题没有帮助。
答案 1 :(得分:0)
(这里我想要聪明:)
trait Idea {
def resolve[A](obj: Obj)
(f: Function1[(Obj { type E = E1 }, E1) forSome { type E1 <: Elem }, A]): A
}
def test(obj: Obj, idea: Idea): Unit = idea.resolve(obj) {
case (x, _: Foo) => withFooObj(x)
case _ =>
}
但Scalac并不认为E1 == Foo
在这里......
答案 2 :(得分:0)
我将专注于您对异构容器的需求,并忽略某些细节,如类型成员和模式匹配。
<强>摘要强>
目标是根据Obj
类型处理elem
。工作解决方案的重要部分:
Obj
一个抽象类,其类型参数代表elem
。Processor
相同类型的参数化elem
。Processor
类型定义隐式Elem
。我考虑将隐式Processor
定义为最小样板(如果你实际上可以称之为样板文件)。你需要定义elem
- 特定的处理器,所以这些隐式对象是组织它们的好地方。
代码:
trait Elem {
// for debugging only
def typeName: String
}
abstract class Obj[E <: Elem](implicit val processor: Processor[E]) {
def elem: E
def process(): Unit = processor.process(this)
}
trait Processor[R <: Elem] {
def process(obj: Obj[R]): Unit
}
object Processor {
implicit val fooProcessor = new Processor[Foo] {
def process(obj: Obj[Foo]): Unit = println(s"Processing Foo: ${obj.elem.typeName}")
}
implicit val barProcessor = new Processor[Bar] {
def process(obj: Obj[Bar]): Unit = println(s"Processing Bar: ${obj.elem.typeName}")
}
implicit val widgetProcessor = new Processor[Widget] {
def process(obj: Obj[Widget]): Unit = println(s"Processing Widget: ${obj.elem.typeName}")
}
}
trait Foo extends Elem { def typeName = "Foo" }
trait Bar extends Elem { def typeName = "Bar" }
trait Widget extends Elem { def typeName = "Widget" }
def test2[R <: Elem](obj: Obj[R]): Unit = obj.process()
// MAIN
val objFoo = new Obj[Foo] {
def elem: Foo = new Foo { }
}
println("===== test2 on a static-typed Foo")
test2(objFoo)
val objBar = new Obj[Bar] {
def elem: Bar = new Bar { }
}
println("===== test2 on a static-typed Bar")
test2(objBar)
val objWidget = new Obj[Widget] {
def elem: Widget = new Widget { }
}
println("===== test2 on a static-typed Widget")
test2(objWidget)
println("===== test2 on a heterogeneous list of `Obj`s")
val heteroList = List(objFoo, objBar, objWidget)
heteroList.foreach(test2(_))
输出:
===== test2 on a static-typed Foo
Processing Foo: Foo
===== test2 on a static-typed Bar
Processing Bar: Bar
===== test2 on a static-typed Widget
Processing Widget: Widget
===== test2 on a heterogeneous list of `Obj`s
Processing Foo: Foo
Processing Bar: Bar
Processing Widget: Widget