我在Scala的ValueSet中发现了一些令人费解的行为。有了这样的枚举,
object MyEnum extends Enumeration{
val V1 = new MyEnum
val V2 = new MyEnum
class MyEnum extends Val
implicit def convertValue(v: Value): MyEnum = v.asInstanceOf[MyEnum]
}
将值MyEnum.values
从Value映射到MyEnum会产生另一个ValueSet,当转换为数组时,它不会保留映射类型。
val naiveMappedValues = MyEnum.values.map(
implicitly[MyEnum.Value => MyEnum.MyEnum]).toArray
// REPL prints: naiveMappedValues: Array[MyEnum.Value] = Array(V1, V2)
如果MyEnum.values
首次转换为列表,则最终数组的类型是正确的。
val mappedValues = MyEnum.values.toList.map(
implicitly[MyEnum.Value => MyEnum.MyEnum]).toArray
// REPL prints: mappedValues: Array[MyEnum.MyEnum] = Array(V1, V2)
为什么会这样?为什么在映射值之前首先将ValueSet转换为List?
答案 0 :(得分:5)
MyEnum.values
会返回Enumeration.ValueSet
。 ValueSet
个子类型Set[Value]
。当您在map
上执行ValueSet
时,它会尝试向您返回一个新的ValueSet
,只要新元素是Value
的子类型,它就可以执行此操作。但是,ValueSet
始终为Set[Value]
,因此当元素向上转换回Value
时,地图内的向下转换将被撤消。另一方面,List
有一个类型参数,因此您可以将List[Value]
映射到List[MyEnum]
。
所有这些魔法都由CanBuildFrom
所需的map
控制。如果您使用collection.breakOut
,则可以强制map
构建您想要的任何类型:
val fastMappedValues: Array[MyEnum.MyEnum] = MyEnum.values.map(
implicitly[MyEnum.Value => MyEnum.MyEnum])(collection.breakOut)