我正在寻找良好的做法,以避免一遍又一遍地重写相同的代码以实现拆箱。说我有这样的事情:
def speedyArrayMaker[@specialized(Long) A: ClassTag](...): Array[A] = {
val builder = Array.newBuilder[A]
// do stuff with builder
builder.result
}
这将在builder
可能的情况下导致我的未装箱存储,但据我了解,没有未装箱的方法调用它,因为我正在经历非专业化ArrayBuilder
特征。
在专门针对 的单态世界中Long
,我会编写val builder = new ArrayBuilder.ofLong()
并完全避免装箱,但没有告诉ArrayBuilder
/Builder
专门针对所有原始类型,我想不出一种避免在这里重复工作的好方法。我想到的一种方法可能是,在speedyArrayMaker
:
val (add, builder): (A => Unit, ArrayBuilder[A]) = implicitly[ClassTag[A]].runtimeClass match {
case java.lang.Long.TYPE =>
val builder = new ArrayBuilder.ofLong()
((x: Long) => builder += x, builder).asInstanceOf
case _ =>
val builder = Array.newBuilder[A]
((x: A) => builder += x, builder)
}
因为它只是+=
我们真正关心的方法,然后我们得到一个专门Function1
用于. 用javap检查,确实我得到了add
Long
90: invokestatic #118; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long;
93: invokeinterface #127, 2; //InterfaceMethod scala/collection/mutable/Builder.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/Builder;
对于Array.newBuilder[A]
版本(甚至在专门的输出中)和:
252: invokeinterface #204, 3; //InterfaceMethod scala/Function1.apply$mcVJ$sp:(J)V
对于复杂的版本。我可以将这种模式打包成一个“专门的构建器助手”函数,但它感觉很难看,尤其是当它仍然在运行时基于专门化期间编译时已知的东西进行调度时。最后我想说我的建议是利用Function1
已经专业化的事实,我不是特别喜欢它。
我可以使用哪些巧妙的技巧来使这更愉快?我意识到这是一个非常低级的细节,很少对性能至关重要,但考虑到所有ArrayBuilder.of*
专业类的工作量/代码重复量,放弃它们的一些优势以换取多态的。
编辑 我想到了一些丑陋的东西,但我希望它能起作用:
def builderOf(x: Array[Int]): ArrayBuilder.ofInt = new ArrayBuilder.ofInt()
def builderOf(x: Array[Long]): ArrayBuilder.ofLong = new ArrayBuilder.ofLong()
def builderOf[A: ClassTag](x: Array[A]): ArrayBuilder[A] = ArrayBuilder.make[A]
然后在我的专门功能中:
val witness: Array[A] = null
val builder = builderOf(witness)
但它似乎builderOf
即使在专用版本中也调用泛型(即使有足够的类型信息可用于调用该Array[Long]
版本)。任何人都知道为什么这不起作用?与我提出的另一种方法相比,这种方法似乎相当干净。我想我希望有一种更“类似宏”的专业化方法,但我想不能保证它对所有实例都是类型正确的,除非它为每个专业化选择相同的方法:(