我们可以使用类型类以通用和类型安全的方式将输入流复制到输出流。类型类是一个概念。这是一种多态的方法。特别地,它是参数多态,因为多态行为是使用参数编码的。在我们的情况下,我们的参数将是Scala traits的通用类型。
让我们创建Reader[I]
和Writer[O]
traits,其中I
和O
分别是输入和输出流类型。
trait Reader[I] {
def read(input: I, buffer: Array[Byte]): Int
}
trait Writer[O] {
def write(output: O, buffer: Array[Byte], startAt: Int, nBytesToWrite: Int): Unit
}
我们现在可以编写一个通用的复制方法,它可以操作订阅这些接口的对象。
object CopyStreams {
type Bytes = Int
def apply[I, O](input: I, output: O, chunkSize: Bytes = 1024)(implicit r: Reader[I], w: Writer[O]): Unit = {
val buffer = Array.ofDim[Byte](chunkSize)
var count = -1
while ({count = r.read(input, buffer); count > 0})
w.write(output, buffer, 0, count)
}
}
请注意这里的隐式r
和w
参数。本质上,我们在说只有当作用域中存在Reader[I]
和Writer[O]
值时,CopyStreams[I,O].apply
才能正常工作。这将使我们能够无缝地调用CopyStreams(input, output)。
然而,需要注意的是,此实现是通用的。它操作独立于实际流实现的类型。
在我的特定用例中,我需要将S3对象复制到本地文件。因此,我创建了以下隐式值。
object Reader {
implicit val s3ObjectISReader = new Reader[S3ObjectInputStream] {
@inline override def read(input: S3ObjectInputStream, buffer: Array[Byte]): Int =
input.read(buffer)
}
}
object Writer {
implicit val fileOSWriter = new Writer[FileOutputStream] {
@inline override def write(output: FileOutputStream,
buffer: Array[Byte],
startAt: Int,
nBytesToWrite: Int): Unit =
output.write(buffer, startAt, nBytesToWrite)
}
}
现在我可以做到以下几点:
val input:S3ObjectStream = ...
val output = new FileOutputStream(new File(...))
import Reader._
import Writer._
CopyStreams(input, output)
// close and such...
如果我们需要复制不同类型的流,只需编写一个新的Reader
或Writer
隐式值即可。我们可以在不改变CopyStreams
代码的情况下使用它!
BufferedInputStream
的原因。 - Maurício Linhares