是否可以执行模式匹配,其结果符合外部方法的类型参数?例如。给出:
trait Key[A] {
def id: Int
def unapply(k: Key[_]): Boolean = k.id == id // used for Fail2
def apply(thunk: => A): A = thunk // used for Fail3
}
trait Ev[A] {
def pull[A1 <: A](key: Key[A1]): Option[A1]
}
trait Test extends Ev[AnyRef] {
val key1 = new Key[String] { def id = 1 }
val key2 = new Key[Symbol] { def id = 2 }
}
是否有Test
(其pull
方法)的实现,它使用key
参数上的模式匹配,并为每个选中的密钥返回Option[A1]
,而不使用是asInstanceOf
?
有些可怜的尝试:
class Fails1 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case `key1` => Some("hallo")
case `key2` => Some('welt)
}
}
class Fails2 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case key1() => Some("hallo")
case key2() => Some('welt)
}
}
class Fails3 extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case k @ key1() => Some(k("hallo"))
case k @ key2() => Some(k('welt))
}
}
没有用,显然......唯一的解决方案是施放:
class Ugly extends Test {
def pull[A1 <: AnyRef](key: Key[A1]): Option[A1] = key match {
case `key1` => Some("hallo".asInstanceOf[A1])
case `key2` => Some('welt .asInstanceOf[A1])
}
}
val u = new Ugly
u.pull(u.key1)
u.pull(u.key2)
答案 0 :(得分:1)
问题确实是模式匹配会忽略所有已擦除的类型。然而,人们可以使用一些隐含的诡计。以下内容将保留返回类型匹配所提供的类型解析。
abstract class UnErased[A]
implicit case object UnErasedString extends UnErased[String]
implicit case object UnErasedSymbol extends UnErased[Symbol]
class UnErasedTest extends Test {
def pull[ A1 <: AnyRef ]( key: Key[ A1 ])(implicit unErased: UnErased[A1]): Option[ A1 ] = unErased match {
case UnErasedString if key1.id == key.id => Some( "hallo" )
case UnErasedSymbol if key2.id == key.id => Some( 'welt )
case _ => None
}
}
val u = new UnErasedTest
println( u.pull( u.key1 ) )
println( u.pull( u.key2 ) )
然而,这几乎等同于仅定义Key的单独子类。 我发现以下方法更可取但是如果现有代码使用Key [String]而无法更改为必要的KeyString(或者需要更改的工作量太多),它可能无效。
trait KeyString extends Key[String]
trait KeySymbol extends Key[Symbol]
trait Test extends Ev[ AnyRef ] {
val key1 = new KeyString { def id = 1 }
val key2 = new KeySymbol { def id = 2 }
}
class SubTest extends Test {
def pull[ A1 <: AnyRef ]( key: Key[ A1 ]): Option[ A1 ] = key match {
case k: KeyString if key1.id == k.id => Some( "hallo" )
case k: KeySymbol if key2.id == k.id => Some( 'welt )
case _ => None
}
}
val s = new SubTest
println( s.pull( s.key1 ) )
println( s.pull( s.key2 ) )
答案 1 :(得分:0)
我在这里提供了一个基于Neil Essy答案的封闭类型方法的扩展示例(显示了我的更多上下文):
trait KeyLike { def id: Int }
trait DispatchCompanion {
private var cnt = 0
sealed trait Value
sealed trait Key[V <: Value] extends KeyLike {
val id = cnt // automatic incremental ids
cnt += 1
}
}
trait Event[V] {
def apply(): Option[V] // simple imperative invocation for testing
}
class EventImpl[D <: DispatchCompanion, V <: D#Value](
disp: Dispatch[D], key: D#Key[V]) extends Event[V] {
def apply(): Option[V] = disp.pull(key)
}
trait Dispatch[D <: DispatchCompanion] {
// factory method for events
protected def event[V <: D#Value](key: D#Key[V]): Event[V] =
new EventImpl[D, V](this, key)
def pull[V <: D#Value](key: D#Key[V]): Option[V]
}
然后,以下场景编译时没有太多杂乱:
object Test extends DispatchCompanion {
case class Renamed(before: String, now: String) extends Value
case class Moved (before: Int , now: Int ) extends Value
private case object renamedKey extends Key[Renamed]
private case object movedKey extends Key[Moved ]
}
class Test extends Dispatch[Test.type] {
import Test._
val renamed = event(renamedKey)
val moved = event(movedKey )
// some dummy propagation for testing
protected def pullRenamed: (String, String) = ("doesn't", "matter")
protected def pullMoved : (Int , Int ) = (3, 4)
def pull[V <: Value](key: Key[V]): Option[V] = key match {
case _: renamedKey.type => val p = pullRenamed; Some(Renamed(p._1, p._2))
case _: movedKey.type => val p = pullMoved; Some(Moved( p._1, p._2))
}
}
...并产生预期的结果:
val t = new Test
t.renamed()
t.moved()
现在我唯一没有得到的,我发现丑陋的是我的案件必须是
的形式case _: keyCaseObject.type =>
并且不能
case keyCaseObject =>
我非常喜欢。这种限制来自哪些想法?