假设我有
trait A
case class S(s:String) extends A
case class B(b:Boolean) extends A
和
val m = scala.collection.mutable.HashMap[String,(Seq[C]) => Option[A]](
"testS" -> ((cs:Seq[C]) => Some(S(foo(cs)))),
"testB" -> ((cs:Seq[C]) => Some(B(bar(cs)))),
...
)
现在假设我们有一个类型D
,使得D <: C
:
val m = scala.collection.mutable.HashMap[String,(Seq[C]) => Option[A]](
"testS" -> ((cs:Seq[C]) => Some(S(foo(cs)))),
"testB" -> ((cs:Seq[C]) => Some(B(bar(cs)))),
"testD" -> ((ds:Seq[D]) => Some(B(baz(ds.head)))), //oops!
...
)
是的,尽管我很愚蠢,但我(再次)忘记了论点应该是相反的,意思是
D <: C, therefore (C => E) <: (D => E)
所以Scala当然不会让我这么做:"类型不匹配">
使用映射的整个想法是,客户端应该能够添加自己的映射。当然,我可以简单地要求像一样添加此类案例
"testD" -> ((ds:Seq[C]) => Some(B(baz(ds.head.asInstanceOf[D]))))
但这是唯一的解决方案吗?
您在这里使用asInstanceOf
是危险的。如果ds.head
实际上是其他D2 <: C
的D2
的实例呢?这不是一个相反的问题,而是你需要完善你的抽象,使类型更有意义。
您应该考虑如何使用字典m
——您是否试图将其所有值应用于某些通用Seq[C]
?如果是,则不应允许客户端传入D => E
类型的函数,因为这些函数可能不适用于C
的所有实例。如果每个客户端都有自己的字典,其中所有方法都将具有单个类型的参数,那么客户端实例化可能在某种类型的X <: C
中是通用的,然后m
可以包括来自X => E
的方法。
当然,提出一个可能在这里工作的类型系统需要更多的细节,因为现在函数列表中唯一的自然类型是Any => E
,这本身就很危险。以下是您的问题的简化版本,可能有助于阐明问题:
import scala.collection.mutable._
trait C
trait D extends C
val cfunc: C => Int = c => 1
val dfunc: D => Int = d => 1
var list = ListBuffer[C => Int](cfunc)
list += dfunc