有效地将AnyVal案例类的序列(Seq [T <:AnyVal])转换为其运行时表示形式

时间:2019-01-14 17:52:26

标签: scala seq value-class

假设我有一个价值案例类

case class Id(i:Int) extends AnyVal

以及包含该值案例类的序列

Seq(Id(1), Id(2), Id(3))

是否有一种方法可以将这些值转换为Int,而无需在序列上进行迭代(例如,通过执行Seq(Id(1), Id(2), Id(3)).map(_.i)

我问的原因是,我认为关于价值案例类的好处是,您可以在运行时使用具有本机类型作为表示形式的价值类,因此非常高效。但是,并非所有使用中的库都支持这些类的自动“转换”。因此,必须传递本机类型,这是简单属性,没什么大不了的,因为编译器可以对其进行优化。但是当有序列时,我必须显式地映射它,这意味着在所有值上都发生了不必要的迭代,因为它实际上除了在运行时映射到相同的值外,什么也不做。有什么方法可以避免这种情况,并在这种情况下使用编译器的某些优化功能?

1 个答案:

答案 0 :(得分:2)

正如阿列克谢·罗曼诺夫(Alexey Romanov)在评论中所猜想的那样,值类存储在Seq中时实际上处于框内。这是javap -c的{​​{1}}的输出:

def bar = Seq(Id(1))

请注意,返回类型为 public scala.collection.Seq<Id> bar(); Code: 0: getstatic #25 // Field scala/collection/Seq$.MODULE$:Lscala/collection/Seq$; 3: getstatic #30 // Field scala/Predef$.MODULE$:Lscala/Predef$; 6: iconst_1 7: anewarray #32 // class Id 10: dup 11: iconst_0 12: new #32 // class Id 15: dup 16: iconst_1 17: invokespecial #35 // Method Id."<init>":(I)V 20: aastore 21: invokevirtual #39 // Method scala/Predef$.genericWrapArray:(Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray; 24: invokevirtual #43 // Method scala/collection/Seq$.apply:(Lscala/collection/Seq;)Lscala/collection/GenTraversable; 27: checkcast #45 // class scala/collection/Seq 30: areturn ,并且在第17行调用了Seq<Id>。鉴于此,没有映射的拆箱是不可能的。

如果接受该提议,此拳击的解决方案将在Scala 3中为Opaque Types。不过,我不确定他们是否会解决您的问题。