什么是Scala Array.apply的魔力

时间:2015-06-23 11:10:00

标签: arrays scala reflection types scala-2.10

来自scala-2.10.4的array.scala,数组定义为

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());
    ...  
}

我对这种翻译感到困惑,引擎盖下的规则是什么?

由于 张

2 个答案:

答案 0 :(得分:3)

Scala Array类只是运行时的假包装器,因此您可以在Scala中使用数组。您可能感到困惑,因为Array类上的那些方法会抛出异常。他们这样做的原因是,如果你真的最终使用假类它会爆炸,因为它应该使用java运行时数组,它没有像Scala这样的正确容器类。你可以看到compiler handles it here的方式。当您在Scala中使用数组时,您可能还使用了pref之类的隐含式ArrayOpsWrappedArray来获取额外的辅助方法。

TLDR :Scala编译器魔术使数组与引擎盖下的java运行时一起工作。

答案 1 :(得分: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)