我对Scala中的嵌套类型有两个问题。
想象一下,我有这种特质;
trait ScanList[E] {
sealed trait Command
case object Recover extends Command
case class Remove(item: E) extends Command
sealed trait Event
case class Removed(item: E) extends Event
}
现在我想在此基础上编写一个通用特征(问题在模式匹配中编码为注释):
trait ScanListProcessor[E] {
type SL = ScanList[E]
def process(msg: SL#Command) = {
msg match {
case u:SL#Remove => // how can instantiate SL#Removed here?
case SL#Recover => //cannot match on nested objects?
}
}
}
使用特征的原因是我可以派生出ScanList的新实现。在这个特性中,我也有像def shouldProcess(item: E): Boolean
这样的操作。对于ScanList[E]
的每个实现,我想编写如上所述的通用行为。
SL#Removed
?我猜它有一个通用参数并试图从中构造一个值,类型类会解决这个问题吗?答案 0 :(得分:4)
Scala中的嵌套特征,类和对象就像Java中的内部类一样。您创建的任何实例都与创建它们的父类/特征的实例相关联。所以继续你的例子:
val sl1 = new ScanList[Int] {}
val sl2 = new ScanList[Int] {}
val r1 = sl1.Removed(1) // has type sl1.Removed
val r2 = sl2.Removed(2) // has type sl2.Removed
val rs = List(r1, r2) // has type List[ScanList[Int]#Removed]
你可以在它们上进行模式匹配,如:
rs match {
case List(sl1.Removed(x), sl2.Removed(y)) => (x, y)
}
模式匹配要求您显式引用嵌套实例所属的父实例。在这里,我们分别匹配sl1.Removed
和sl2.Removed
。
关于第二个问题,如果没有可用的父实例,则无法创建SL#Removed
。只需一个,它就像sl1.Removed(1)
一样简单。
您的ScanListProcessor
可以重写,以便将ScanList
作为值进行操作:
class ScanListProcessor[E](val sl: ScanList[E]) {
def process(msg: sl.Command) = {
msg match {
case sl.Remove(x) => sl.Removed(x)
case sl.Recover => ???
}
}
}
但这很尴尬,这里可能不需要嵌套类型。如果你想做的就是在命名空间中将一些特征,类等组合在一起,然后将它们放在package
或object
而不是trait
或class
中。在这种情况下,您需要将类型参数E
向下移动到Remove
和Removed
本身:
object ScanList {
sealed trait Command
case object Recover extends Command
case class Remove[E](item: E) extends Command
sealed trait Event
case class Removed[E](item: E) extends Event
}