Scala(或Java)中泛型函数的专门化
可以在Scala中专门化泛型函数(或类)吗?例如,我想编写一个将数据写入ByteBuffer的通用函数:Scala(或Java)中泛型函数的专门化,java,scala,generics,specialized-annotation,Java,Scala,Generics,Specialized Annotation,可以在Scala中专门化泛型函数(或类)吗?例如,我想编写一个将数据写入ByteBuffer的通用函数: def writeData[T](buffer: ByteBuffer, data: T) = buffer.put(data) 但是,由于put方法只需要一个字节并将其放入缓冲区,因此我需要将其专门化为int和long,如下所示: def writeData[Int](buffer: ByteBuffer, data: Int) = buffer.putInt(data) def wri
def writeData[T](buffer: ByteBuffer, data: T) = buffer.put(data)
但是,由于put方法只需要一个字节并将其放入缓冲区,因此我需要将其专门化为int和long,如下所示:
def writeData[Int](buffer: ByteBuffer, data: Int) = buffer.putInt(data)
def writeData[Long](buffer: ByteBuffer, data: Long) = buffer.putLong(data)
而且它不会编译。当然,我可以分别编写3个不同的函数writeByte、writeInt和writeLong,但假设数组还有另一个函数:
def writeArray[T](buffer: ByteBuffer, array: Array[T]) {
for (elem <- array) writeData(buffer, elem)
}
def writeArray[T](缓冲区:字节缓冲区,数组:数组[T]){
对于(elem这个怎么样:
def writeData(buffer: ByteBuffer, data: AnyVal) {
data match {
case d: Byte => buffer put d
case d: Int => buffer putInt d
case d: Long => buffer putLong d
...
}
}
在这里,您可以在writeData
方法中区分大小写,这使得所有其他方法都非常简单:
def writeArray(buffer: ByteBuffer, array: Array[AnyVal]) {
for (elem <- array) writeData(buffer, elem)
}
顺便说一句,由于Scala严格的面向对象特性,这项工作非常容易。在Java中,原语类型不是对象,这将非常麻烦。在Java中,您实际上必须为每个原语类型创建一个单独的方法,除非您想执行一些难看的装箱和拆箱操作。使用类型类模式。它有与instanceOf check(或模式匹配)相比,它的优点是类型安全
import java.nio.ByteBuffer
trait BufferWriter[A] {
def write(buffer: ByteBuffer, a: A)
}
class BuffPimp(buffer: ByteBuffer) {
def writeData[A: BufferWriter](data: A) = {
implicitly[BufferWriter[A]].write(buffer, data)
}
}
object BuffPimp {
implicit def intWriter = new BufferWriter[Int] {
def write(buffer: ByteBuffer, a: Int) = buffer.putInt(a)
}
implicit def doubleWriter = new BufferWriter[Double] {
def write(buffer: ByteBuffer, a: Double) = buffer.putDouble(a)
}
implicit def longWriter = new BufferWriter[Long] {
def write(buffer: ByteBuffer, a: Long) = buffer.putLong(a)
}
implicit def wrap(buffer: ByteBuffer) = new BuffPimp(buffer)
}
object Test {
import BuffPimp._
val someByteBuffer: ByteBuffer
someByteBuffer.writeData(1)
someByteBuffer.writeData(1.0)
someByteBuffer.writeData(1L)
}
因此,这段代码并不是TypeClass的最佳演示。我对它们还是很陌生。这段视频对它们的优点以及如何使用它们进行了非常全面的概述:
声明
def writeData[Int](buffer: ByteBuffer, data: Int)
def writeData[Long](buffer: ByteBuffer, data: Long)
不要编译,因为Int和Long是等价的,因为Int和Long是形式的
类型参数,而不是标准的Scala类型。要定义具有标准Scala类型的函数,只需编写:
def writeData(buffer: ByteBuffer, data: Int) = buffer.putInt(data)
def writeData(buffer: ByteBuffer, data: Long) = buffer.putLong(data)
通过这种方式,可以使用相同的名称声明不同的函数
由于它们是不同的函数,您无法将它们应用于静态未知类型列表的元素。您必须首先确定列表的类型。请注意,可能会发生这种情况:列表的类型为AnyRef,然后您必须动态确定每个元素的类型。可以使用原始代码,或模式匹配,如rolve
所建议的。我认为这将产生相同的字节码
总之,您必须选择:
- 具有多种功能的快速代码,如
writeByteArray、writeInArray
等。它们都可以具有相同的名称writeArray
,但可以通过其实际参数进行静态区分。Dominic Bou Sa建议的变体就是这种类型
- 简洁但速度慢的代码,具有运行时类型确定功能
不幸的是,你不能同时拥有快速和简洁的代码。如果你能同时拥有一个紧凑和高效的解决方案,那不是很好吗?事实证明,考虑到Scala的@specialized
功能,你可以做到。首先,警告:该功能有点缺陷,如果你试图将其用于过于复杂的事情,它可能会崩溃。但对于这一点就我的情况而言,这几乎是完美的
@specialized
注释为每个基元类型创建单独的类和/或方法,然后在编译器确定基元类型是什么时调用该类和/或方法,而不是泛型版本。唯一的缺点是它完全自动完成所有这一切--您不必填写自己的方法。这就是k这很遗憾,但是使用类型类可以克服这个问题
让我们看一些代码:
import java.nio.ByteBuffer
trait BufferWriter[@specialized(Byte,Int) A]{
def write(b: ByteBuffer, a: A): Unit
}
class ByteWriter extends BufferWriter[Byte] {
def write(b: ByteBuffer, a: Byte) { b.put(a) }
}
class IntWriter extends BufferWriter[Int] {
def write(b: ByteBuffer, a: Int) { b.putInt(a) }
}
object BufferWriters {
implicit val byteWriter = new ByteWriter
implicit val intWriter = new IntWriter
}
这给了我们一个BufferWriter
特性,它是泛型的,但是我们覆盖了我们想要的每个特定的基元类型(在本例中是Byte
和Int
)使用适当的实现。专门化足够聪明,可以将此显式版本与它通常用于专门化的隐藏版本连接起来。因此,您有自定义代码,但如何使用它?这就是隐式VAL的作用(我这样做是为了速度和清晰度):
如果您将这些东西放在一个文件中,并开始使用javap-c-private
查看类,您将看到使用了适当的原语方法
(请注意,如果不使用专门化,此策略仍然有效,但必须在循环中装箱值才能将数组复制出来。)等待3…2…1.中的类型类答案。添加了专用的标记,因为这在Scala中意味着某种特定的东西,而这正是您所需要的。@RexKerr:我不认为@specialized
在这里有什么关系,它无助于选择putInt
,putLong
,“@TravisBrown-不,但这样做会让你不必为自己这样做而感到遗憾,而不是每次都从头开始实现所有东西。向缓冲区添加字节并不是你通常想要调用装箱的事情。你应该在示例中添加对数组的支持。现在看来,这似乎是一种不必要的复杂方式。”ng旧的重载可以实现什么。只要您尝试处理复合结构,类型类模式就会显示出它的优势:使用此模式,无需定义WriteInArray、writeLongArray等。只需编写一个通用writeArray meyhod,它对元素类型+1采用隐式BufferWriter实例,只需很少的ob注意:您可以使用隐式对象intWriter[Int]{…}
对于非泛型的实例,在我看来,它使意图更加清晰,并且可能更有效。+1-这是一个很好的答案,但还有一个额外的缺陷,使它能够以全速工作,即使使用数组也是如此。这并没有让我觉得是一个重大变化(这只是稍微更惯用,更有效),尤其是当他明确要求清理时。他深思熟虑的回答是完整的,只是稍微增强了。正如我上面所建议的,添加对数组的支持更像是对答案的实际更改,这就是为什么我没有自己做,并建议将更改作为注释。没问题!Typecl
def writeData(buffer: ByteBuffer, data: Int) = buffer.putInt(data)
def writeData(buffer: ByteBuffer, data: Long) = buffer.putLong(data)
import java.nio.ByteBuffer
trait BufferWriter[@specialized(Byte,Int) A]{
def write(b: ByteBuffer, a: A): Unit
}
class ByteWriter extends BufferWriter[Byte] {
def write(b: ByteBuffer, a: Byte) { b.put(a) }
}
class IntWriter extends BufferWriter[Int] {
def write(b: ByteBuffer, a: Int) { b.putInt(a) }
}
object BufferWriters {
implicit val byteWriter = new ByteWriter
implicit val intWriter = new IntWriter
}
import BufferWriters._
def write[@specialized(Byte,Int) A: BufferWriter](b: ByteBuffer, ar: Array[A]) {
val writer = implicitly[BufferWriter[A]]
var i = 0
while (i < ar.length) {
writer.write(b, ar(i))
i += 1
}
}
val b = ByteBuffer.allocate(6)
write(b, Array[Byte](1,2))
write(b, Array[Int](0x03040506))
scala> b.array
res3: Array[Byte] = Array(1, 2, 3, 4, 5, 6)