Scala数组的神奇之处是什么?

3

从scala-2.10.4的array.scala中,Array被定义为

final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable {    
  /** The length of the array */
  def length: Int = throw new Error()
  def apply(i: Int): T = throw new Error()
  def update(i: Int, x: T) { throw new Error() }
  override def clone(): Array[T] = throw new Error()
}

请注意,apply方法会抛出异常!对于伴随对象Arrry, 我找到了以下代码:

  def apply[T: ClassTag](xs: T*): Array[T] = {
    val array = new Array[T](xs.length)
    var i = 0
    for (x <- xs.iterator) { array(i) = x; i += 1 }
    array
  }

我知道有一个隐式参数,它是 ClassTag[T],令我惊讶的是下面这行代码:

new Array[T] (xs.length)

居然能够编译通过。通过反编译Array.class文件,我发现这行代码被转换成了:

public <T> Object apply(Seq<T> xs, ClassTag<T> evidence$2)
{
    // evidence$2 is implicit parameter
    Object array = evidence$2.newArray(xs.length());
    ...  
}

我对这种翻译方式感到非常困惑,它的规则是什么?

谢谢 张


ClassTag[T]具有一个名为newArray的方法,用于创建新的Array[T] - Peter Neyens
@PeterNeyens,我知道有一个newArray方法,但我不知道为什么编译器选择了newArray。 - Chang
引用 Manifest[T] 的第一行(ClassTag[T]的子类):“_它的支持使用是为了以Class实例的形式访问类型的擦除,如果在编译时不知道类,则必须这样做才能创建本机数组._” - Peter Neyens
@PeterNeyens,我想知道隐式参数是如何使用的?如果你看一下def apply[T: ClassTag](xs: T*): Array[T],我没有找到任何语法规则可以使*转换为new。 - Chang
@PeterNeyens 请看一下 * val array = new ArrayT *,应用了哪个语法规则,以便我们可以将 new Array[T] 翻译成 evidence$2.newArray。 - Chang
Scala中的数组被编译器进行了大量的优化和修改。Array.scala文件中的代码或反编译后的类只是作为参考,不要直接使用。 - SwiftMango
2个回答

5
Scala的Array类只是一个对运行时的伪装,这样你就可以在Scala中使用数组。你可能会感到困惑,因为Array类上的那些方法会抛出异常。他们这样做的原因是,如果你真的使用了这个假类,它就会崩溃,因为实际上应该使用java运行时数组,而java运行时数组没有像Scala一样的适当的容器类。你可以看一下编译器在这里处理的方式。当你在Scala中使用数组时,你可能还会使用一些来自predef的隐式对象,如ArrayOpsWrappedArray等辅助方法。

简而言之: Scala编译器通过魔法使数组与java运行时配合工作。


1

在JVM上,数组不受类型擦除的影响,在运行时,Array[_]Array[Int]Array[String]Array[AnyRef] 存在差异。与Java不同,Scala可以处理这种情况并且实现透明,因此...

class Foo {
  val foo = new Array[Int](123)
}

有一个直接的字节码调用来创建整数数组,而

class Bar[A](implicit ev: reflect.ClassTag[A]) {
  val bar = new Array[A](123)
}

通过使用类型为ClassTag[A]的隐式类型证据参数来解决,以便在运行时JVM仍然可以创建正确的数组。这被转换为您看到的调用ev.newArray(123)

据我所知,要引用这样的证据参数,我们需要像这样的东西:val tag = implicitly[ClassTag[T]]; tag.newArray(123)。然而,在这种情况下,new Array[T] 直接被翻译为 ev.newArray(123)。我无法相信 Scala 编译器在遇到 new Array[T] 时会执行某些神奇的操作。 - Chang
1
换句话说,标题中关于Scala Array.apply的魔法的问题的答案是“它确实是魔法”。 - Jörg W Mittag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接