通用流/读取器复制

时间:2013-09-26 14:53:42

标签: scala

使用流,我编写了以下函数,将InputStream的内容复制到OutputStream

def copy(input: InputStream, output: OutputStream, chunk: Int = 2048) {
  val buffer = Array.ofDim[Byte](chunk)
  var count  = -1

  while({count = input.read(buffer); count > 0})
    output.write(buffer, 0, count)
}

编写相同的函数将Reader的内容复制到Writer会产生非常相似的代码 - 唯一的区别是Array的类型。

我正在尝试,但到目前为止还无法推广我的copy方法。

我想我记得在某处可以通过它支持的方法描述一种类型 - 接受任何支持read(buffer: Array[Byte])的类型,比方说。我可能已经成功了,因为我再也找不到了。

我还想过为各种集合'Ordering方法传递一个隐式的辅助对象,有点像sorted,但是我的大脑正在撞墙。

任何有关正确方向的帮助或指示都将受到赞赏。

2 个答案:

答案 0 :(得分:3)

是的,你想要一个structured type。请注意,这将利用运行时反射。这不应该是一个明显的性能打击,因为你可能在这个函数中做I / O,但仅仅是FYI。

def copy(input: { def read(buffer: Array[Byte]):Int }, ...

答案 1 :(得分:2)

您可以获得结构化类型的许多好处,而不会因使用类型类而导致运行时损失。类型类是一个概念。这是多态性的一种方法。特别是,它是ad-hoc polymorphism。它是我们获得编译时,静态检查鸭子打字的方式!

让我们制作Reader[I]Writer[O]个特征,其中IO分别是输入和输出流类型。

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)
  }
}

请注意隐含的rw参数。从本质上讲,如果范围中有CopyStreams[I,O].applyReader[I]值,我们会说Writer[O]会起作用。这将使我们能够无缝地调用CopyStreams(输入,输出)。

我们可以使用context bounds并将apply签名重写为

def apply[I: Reader, O: Writer](input: I, output: O, chunkSize: Bytes = 1024): Unit

然后,我们将分别使用Reader[I]Writer[O]访问我们的implicitly[Reader[I]]implicitly[Writer[O]]个实例。请注意,此表单等同于第一个表单。 Scala编译器实际上会将这个上下文绑定版本重新编写为我们提供的第一个版本。

但重要的是,请注意此实现是通用的。它对独立于实际流实现的类型进行操作。

在我的特定用例中,我需要将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...

如果我们需要复制不同的流类型,我们只需要编写一个新的ReaderWriter隐式值。我们可以使用CopyStreams代码而无需更改它!