首页 > 解决方案 > 有效地将 AnyVal 案例类 (Seq[T <: AnyVal]) 的序列转换为其运行时表示

问题描述

假设我有一个价值案例类

case class Id(i:Int) extends AnyVal

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

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

有没有一种方法可以将这些值转换为Int不需要对序列进行迭代的方法(例如,通过Seq(Id(1), Id(2), Id(3)).map(_.i)?

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

标签: scalaseqvalue-class

解决方案


正如 Alexey Romanov 在评论中推测的那样,值类在存储在Seq. 这是javap -cfor的输出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

请注意,返回类型是Seq<Id>并且Id."<init>"在第 17 行被调用。鉴于此,没有映射的拆箱是不可能的。

如果该提议被接受,则此拳击的解决方案将是Scala 3 中的不透明类型。不过,我不确定他们是否会解决您的问题。


推荐阅读