使用密封特性作为地图的关键

时间:2015-02-27 19:43:44

标签: scala traits

我正在尝试从密封特征的实例中定义一个地图。在以下代码中,Scala似乎将密钥类型推断为Product with Serializable with 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)

我可以在m的定义中更改密钥类型,但这意味着在许多地方重复Product with Serializable with Day。我发现的另一个选择是将特性的定义改为:

sealed trait Day extends Product with Serializable

由于使用密封特征和案例对象而不是枚举有许多优点,我想知道将它们作为键放在地图中的好方法。

2 个答案:

答案 0 :(得分:4)

由于Map需要密钥具有ProductSerializable中定义的属性,因此Scala隐式创建anonymous class,使用Product扩展您的类和Serializable提供equalshash的默认实现。

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)
}

答案 1 :(得分:1)

您看到这的根本原因是

  1. 默认情况下,编译器会推断最具体的类型,在这种情况下为Product with Serializable with Test.DayProductSerializable类由Scala中的所有case类和case对象隐式实现,因此所有case对象都具有相同的属性。因此,这确实是SundayMonday最具体的常见类型,

  2. 另一方面,
  3. 特征既不实现Product也不实现Serializable

因此,当您需要在某个地方Day时,将无法使用更具体的推断类型(也是因为错误消息指出,K的{​​{1}}类型参数是不变的)

一种方法,就像萨尔维什·库马尔·辛格(Sarvesh Kumar Singh)的回答所表明的那样,是使你的性格扩展MapProduct,但是在我看来,更好的方法(由吕迪格·克拉恩(RüdigerKlaehn)指出)是通过显式地告诉编译器您实际上可以使用更通用的类型Serializable

Map[Day, Int]