Scala 使用密封特征作为地图的键

Scala 使用密封特征作为地图的键,scala,traits,Scala,Traits,我试图从一个封闭特征的实例中定义一个映射。在下面的代码中,Scala似乎将密钥类型推断为可序列化为Day的产品: object Test extends App { sealed trait Day case object Sunday extends Day case object Monday extends Day case object Tuesday extends Day val m: Map[Day, Int] = Map(Sunday -> 17, M

我试图从一个封闭特征的实例中定义一个映射。在下面的代码中,Scala似乎将密钥类型推断为可序列化为Day的
产品

object Test extends App {
  sealed trait Day
  case object Sunday extends Day
  case object Monday extends Day
  case object Tuesday extends Day

  val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)
}
这不会编译:

Test.scala:7: error: type mismatch;
 found   : scala.collection.immutable.Map[Product with Serializable with Test.Day,Int]
 required: Map[Test.Day,Int]
Note: Product with Serializable with Test.Day <: Test.Day, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Test.Day`. (SLS 3.2.10)
  val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)

由于使用密封的trait和case对象而不是enum有很多优点,我想知道将它们作为键放入映射中的好方法是什么。

因为
map
需要键具有在
Product
Serializable
中定义的属性,因此Scala隐式创建了
匿名类
,该类使用
产品
可序列化
扩展类,后者提供了
等于
哈希
的默认实现

object Test extends App {
  trait PS extends Product with Serializable
  sealed trait Day extends PS
  case object Sunday extends Day
  case object Monday extends Day
  case object Tuesday extends Day

  val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)
}

你看到这一点的根本原因是

  • 默认情况下,编译器会推断出最具体的类型,在本例中是可通过Test.Day序列化的
    产品。
    Product
    Serializable
    类由Scala中的所有case类和case对象隐式实现,因此所有case对象都有它们的共同点。因此,这确实是
    周日
    周一
    最常见的类型

  • 另一方面,traits不实现
    产品
    可序列化

  • 因此,当您需要某处
    Day
    时,更具体的推断类型将不起作用(也因为,如错误消息所示,
    Map
    K
    类型参数是不变的。)

    一种方法,正如Sarvesh Kumar Singh的回答所指出的那样,是让您的trait扩展
    产品和
    可序列化
    ,但在我看来,更好的方法(由Rüdiger Klaehn指出)是通过使类型显式化,告诉编译器您实际上可以使用更通用的
    映射[Day,Int]

    val m = Map[Day, Int](Sunday -> 17, Monday -> 4).withDefaultValue(0)
    

    因为Map需要键具有在
    Product
    Serializable
    中定义的属性,因此Scala隐式地创建了匿名类,该类使用
    Product
    Serializable
    扩展类,后者提供了
    equals
    hash
    的默认实现。类型推断只需要一些帮助。这是有效的:
    val m:Map[Day,Int]=Map[Day,Int](星期日->17,星期一->4)。使用默认值(0)
    val m = Map[Day, Int](Sunday -> 17, Monday -> 4).withDefaultValue(0)