Мы можем скопировать входной поток в выходной поток общим и безопасным для типов способом, используя классы типов.Класс типов - это понятие.Это один из подходов к полиморфизму.В частности, это параметрический полиморфизм , потому что полиморфное поведение кодируется с использованием параметров.В нашем случае наши параметры будут универсальными типами для черт Scala.
Давайте сделаем Reader[I]
и Writer[O]
черты, где 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
.По сути, мы говорим, что CopyStreams[I,O].apply
будет работать, если в области видимости есть значения Reader[I]
и Writer[O]
.Это позволит нам беспрепятственно вызывать 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
без его изменения!