使用密封特征作为映射的键
Using sealed trait as a key for a map
我正在尝试根据密封特性的实例定义地图。在下面的代码中,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
由于使用密封特征和案例对象而不是枚举有很多优点,我想知道将它们作为键放入映射中的好方法是什么。
因为 Map
需要键具有在 Product
和 Serializable
中定义的属性,所以 Scala 隐式创建 anonymous class
扩展你的 class Product
和 Serializable
提供了 equals
和 hash
.
的默认实现
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)
}
你看到这个的根本原因是
编译器默认推断最具体的类型,在本例中为 Product with Serializable with Test.Day
。 Product
和 Serializable
类 由所有 case 类 和 Scala 中的 case 对象隐式实现,因此所有 case 对象都有它们的共同点。因此,这确实是 Sunday
和 Monday
、
中最具体的常见类型
另一方面,不实现 Product
或 Serializable
.
因此,当您在某处需要 Day
时,更具体的推断类型将不起作用(还因为,如错误消息所示,Map
的 K
类型参数是不变的。)
一种方法,如 Sarvesh Kumar Singh 的回答所示,是让你的特征扩展 Product
和 Serializable
,但在我看来更好的方法(Rüdiger Klaehn 指出)是告诉编译器你实际上可以通过使类型显式 Map[Day, Int]
来使用更通用的类型:
val m = Map[Day, Int](Sunday -> 17, Monday -> 4).withDefaultValue(0)
我正在尝试根据密封特性的实例定义地图。在下面的代码中,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
由于使用密封特征和案例对象而不是枚举有很多优点,我想知道将它们作为键放入映射中的好方法是什么。
因为 Map
需要键具有在 Product
和 Serializable
中定义的属性,所以 Scala 隐式创建 anonymous class
扩展你的 class Product
和 Serializable
提供了 equals
和 hash
.
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)
}
你看到这个的根本原因是
编译器默认推断最具体的类型,在本例中为
中最具体的常见类型Product with Serializable with Test.Day
。Product
和Serializable
类 由所有 case 类 和 Scala 中的 case 对象隐式实现,因此所有 case 对象都有它们的共同点。因此,这确实是Sunday
和Monday
、另一方面,不实现
Product
或Serializable
.
因此,当您在某处需要 Day
时,更具体的推断类型将不起作用(还因为,如错误消息所示,Map
的 K
类型参数是不变的。)
一种方法,如 Sarvesh Kumar Singh 的回答所示,是让你的特征扩展 Product
和 Serializable
,但在我看来更好的方法(Rüdiger Klaehn 指出)是告诉编译器你实际上可以通过使类型显式 Map[Day, Int]
来使用更通用的类型:
val m = Map[Day, Int](Sunday -> 17, Monday -> 4).withDefaultValue(0)