Scala 特征、超类和早期定义语法
Scala trait, superclass and early definition syntax
我正在通过书中的练习来学习 Scala "Scala for the Impatient"。一项练习要求:
In the java.io
library, you add buffering to an input stream with a
BufferedInputStream
decorator. Reimplement buffering as a trait. For
simplicity, override the read
method.
尽管(对我而言)我不清楚我应该覆盖哪个 read
方法(在新创建的特征或输入流中),但我想出了以下方法:
trait Buffered {
self: InputStream =>
val bufferSize: Int = 1024
val buff = new Array[Byte](bufferSize)
/* Number of bytes read so far */
var pos = 0
/* Number of bytes stored in the buffer */
var count = -1
private def fill() = {
self.read(buff)
}
def readBuffered(): Int = {
if (isAvailableInBuffer) {
println("Data available in buffer.")
incrementAndGet()
} else {
println("Data not available in buffer.")
count = fill()
println(f"Read $count%d bytes into buffer.")
if (isAvailableInBuffer) incrementAndGet() else count
}
}
private def isAvailableInBuffer = {
count > (pos % bufferSize)
}
private def incrementAndGet() = {
val x = buff(pos % bufferSize)
pos += 1
x
}
}
class MyBufferedInputStream(val input: InputStream, val size: Int) extends InputStream with Buffered {
override def read() = {
input.read()
}
}
问题:
- 如何在 class
MyBufferedInputStream
中指定 val bufferSize
的早期定义?所有在线示例都显示了具有单个特征的早期定义,而不是多个 classes 和特征。
- 对于问题的要求,这看起来是正确的方法吗?
编辑:
根据 Kulu Limpa 的回答,我修改了我的代码。以下是有效的(忽略日志内容):
trait Buffered extends Logged {
self: InputStream =>
val bufferSize: Int = 1024
val buff = new Array[Byte](bufferSize)
/* Number of bytes read so far */
var pos = 0
/* Number of bytes stored in the buffer */
var count = 0
def readBuffered(): Int = {
if (isTimeToRefillBuffer) {
log(f"Time to refill buffer. bufferSize = $bufferSize%d, pos = $pos%d, count = $count%d")
count = fill()
}
if (isDataAvailableInbuffer) {
log(f"Data available in buffer. bufferSize = $bufferSize%d, pos = $pos%d, count = $count%d")
getFromBuffer()
} else -1
}
/* Invoke the read method of the InputStream it'll be mixed in with */
private def fill() = self.read(buff)
private def isDataAvailableInbuffer = count > 0
private def isTimeToRefillBuffer = !isDataAvailableInbuffer
private def getFromBuffer() = {
val x = buff(pos % bufferSize)
pos += 1
count -= 1
x
}
}
class MyBufferedInputStream(val input: InputStream, override val bufferSize: Int) extends InputStream with Buffered {
override def read() = {
input.read()
}
}
测试:
class MyBufferedInputStreamSpec extends UnitSpec {
"Buffer size" should "be overridable and 4" in {
new MyBufferedInputStream(newInputStream("abc"), 4).bufferSize should be(4)
}
"Buffered Stream" should "read data twice into buffer when bufferSize is smaller than number of bytes available" in {
val str = new MyBufferedInputStream(newInputStream("abc"), 2) with ConsoleLogger with TimestampLogger
str.readBuffered() should be('a')
str.readBuffered() should be('b')
str.readBuffered() should be('c')
str.readBuffered() should be(-1)
}
"Buffered Stream" should "read data once into buffer when bufferSize is larger than number of bytes available" in {
val str = new MyBufferedInputStream(newInputStream("abc"), 4) with ConsoleLogger with TimestampLogger
str.readBuffered() should be('a')
str.readBuffered() should be('b')
str.readBuffered() should be('c')
str.readBuffered() should be(-1)
}
private def newInputStream(str: String) = {
new ByteArrayInputStream(str.getBytes(UTF_8))
}
}
编辑:
使用预初始化字段实现 class 的语法是
class ImplementingClass extends {val field1 = ???; val field2 = ???} with AbstractSuperclass with Trait1 with Trait2
请注意,尽管有关键字 with
,(抽象)superclass 需要位于第一个位置,并且您不能扩展多个 class。
在示例中,
class MyBufferedInputStream(val input: InputStream, size: Int) extends {val bufferSize: Int = size} with InputStream with Buffered
有效,但在这种情况下使 bufferSize
成为构造函数参数更具可读性
class MyBufferedInputStream(val input: InputStream, val bufferSize: Int) extends InputStream with Buffered
How do I specify an early definition for val bufferSize
in class MyBufferedInputStream
? All examples online show early definition with a single trait, not with multiple classes and traits.
我认为早期定义 - 或 Chapter 20.5 in Programming in Scala, First Edition 中所谓的 "Pre-initialized fields" - 基于你混合的特征数量没有区别。但是让我们通过示例:
让我们假设您没有在 Buffered
中初始化 bufferSize
,即
trait Buffered {
self: InputStream =>
val bufferSize: Int
val buff = new Array[Byte](bufferSize)
/* ... */
}
首先,实现缓冲输入流的错误方法:
class WrongBufferedInputStream(val input: InputStream) extends InputStream with Buffered {
val bufferSize = 1024
override def read() = input.read()
}
因为superclass的字段,即Buffered.buff
在subclass的字段之前被初始化,所以new Array[Byte](bufferSize)
在[=22=之前被调用] 被初始化,因此当 bufferSize
是 0
时,导致长度为零的缓冲区。
这个问题有多种解决方案:
- make
bufferSize
在实现 class a lazy val
所以它在第一次使用时被初始化
- 使用预初始化字段
- 使
bufferSize
成为构造函数参数
代码:
class MyBufferedInputStreamLazyInitialization(val input: InputStream) extends InputStream with Buffered {
lazy val bufferSize = 1024
override def read() = input.read()
}
class MyBufferedInputStream(val input: InputStream) extends {val bufferSize: Int = 1024} with InputStream with Buffered {
override def read() = input.read()
}
class MyBufferedInputStreamWithCustomSize(val input: InputStream, val bufferSize: Int = 1024) extends InputStream with Buffered {
override def read() = input.read()
}
用法示例:
println(new WrongBufferedInputStream(System.in).buff.length) // 0
println(new MyBufferedInputStreamLazyInitialization(System.in).buff.length) // 1024
println(new MyBufferedInputStream(System.in).buff.length) // 1024
println(new MyBufferedInputStreamWithCustomSize(System.in).buff.length) // 1024
println(new MyBufferedInputStreamWithCustomSize(System.in, 512).buff.length) // 512
Does this look like a correct approach to what the question is asking for?
我没有 Scala for the Impatient 的副本,但我觉得这个方法不错。
多亏了自我类型,你可以注入任何 InputStream
并创建,例如,缓冲文件输入流:
class BufferedFileInputStream(file: File) extends {val bufferSize = 1024} with FileInputStream(file) with Buffered
您还可以创建 InputStream
的其他装饰器并将它们混合在一起,例如
trait Awesomeness {
self: InputStream =>
/* add some awesome functionality */
}
class BufferedAwesomeInputStream(val input: InputStream) extends {val bufferSize = 1024} with InputStream with Buffered with Awesomeness {
override def read() = input.read()
}
虽然方法看起来不错,但具体实现有点奇怪。例如,readBuffered
returns 要么是当前位置的缓冲值,要么是缓冲区中存储的字节数,巧合的是,它们都可以看作是一个 Int
,但在概念上是不同的.此外,在 public 接口中公开 var
字段和可变数组 buff
是危险的,因为这使客户端代码能够改变 Buffered
的内部状态。
我理解 InputStream.read()
return 是 "code" -1
如果到达流的末尾。然而,如果缓冲区中没有可用数据,更惯用的 readBuffered()
实现将 return 和 Option[Byte]
、returning None
:
def readBuffered(): Option[Byte] = {
if (isAvailableInBuffer) {
Some(incrementAndGet())
} else {
count = fill()
if (isAvailableInBuffer) Some(incrementAndGet()) else None
}
}
用法示例:
val f = new MyBufferedInputStream(new InputStream {
val data = "hello" map (_.toByte)
var pos = 0
override def read(): Int = if (pos < data.length){pos = pos + 1; data(pos - 1).asInstanceOf[Int]} else -1
}, 1024)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(h)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(e)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(l)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(l)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(o)
println(f.readBuffered() map (_.asInstanceOf[Char])) // None
我正在通过书中的练习来学习 Scala "Scala for the Impatient"。一项练习要求:
In the
java.io
library, you add buffering to an input stream with aBufferedInputStream
decorator. Reimplement buffering as a trait. For simplicity, override theread
method.
尽管(对我而言)我不清楚我应该覆盖哪个 read
方法(在新创建的特征或输入流中),但我想出了以下方法:
trait Buffered {
self: InputStream =>
val bufferSize: Int = 1024
val buff = new Array[Byte](bufferSize)
/* Number of bytes read so far */
var pos = 0
/* Number of bytes stored in the buffer */
var count = -1
private def fill() = {
self.read(buff)
}
def readBuffered(): Int = {
if (isAvailableInBuffer) {
println("Data available in buffer.")
incrementAndGet()
} else {
println("Data not available in buffer.")
count = fill()
println(f"Read $count%d bytes into buffer.")
if (isAvailableInBuffer) incrementAndGet() else count
}
}
private def isAvailableInBuffer = {
count > (pos % bufferSize)
}
private def incrementAndGet() = {
val x = buff(pos % bufferSize)
pos += 1
x
}
}
class MyBufferedInputStream(val input: InputStream, val size: Int) extends InputStream with Buffered {
override def read() = {
input.read()
}
}
问题:
- 如何在 class
MyBufferedInputStream
中指定val bufferSize
的早期定义?所有在线示例都显示了具有单个特征的早期定义,而不是多个 classes 和特征。 - 对于问题的要求,这看起来是正确的方法吗?
编辑: 根据 Kulu Limpa 的回答,我修改了我的代码。以下是有效的(忽略日志内容):
trait Buffered extends Logged {
self: InputStream =>
val bufferSize: Int = 1024
val buff = new Array[Byte](bufferSize)
/* Number of bytes read so far */
var pos = 0
/* Number of bytes stored in the buffer */
var count = 0
def readBuffered(): Int = {
if (isTimeToRefillBuffer) {
log(f"Time to refill buffer. bufferSize = $bufferSize%d, pos = $pos%d, count = $count%d")
count = fill()
}
if (isDataAvailableInbuffer) {
log(f"Data available in buffer. bufferSize = $bufferSize%d, pos = $pos%d, count = $count%d")
getFromBuffer()
} else -1
}
/* Invoke the read method of the InputStream it'll be mixed in with */
private def fill() = self.read(buff)
private def isDataAvailableInbuffer = count > 0
private def isTimeToRefillBuffer = !isDataAvailableInbuffer
private def getFromBuffer() = {
val x = buff(pos % bufferSize)
pos += 1
count -= 1
x
}
}
class MyBufferedInputStream(val input: InputStream, override val bufferSize: Int) extends InputStream with Buffered {
override def read() = {
input.read()
}
}
测试:
class MyBufferedInputStreamSpec extends UnitSpec {
"Buffer size" should "be overridable and 4" in {
new MyBufferedInputStream(newInputStream("abc"), 4).bufferSize should be(4)
}
"Buffered Stream" should "read data twice into buffer when bufferSize is smaller than number of bytes available" in {
val str = new MyBufferedInputStream(newInputStream("abc"), 2) with ConsoleLogger with TimestampLogger
str.readBuffered() should be('a')
str.readBuffered() should be('b')
str.readBuffered() should be('c')
str.readBuffered() should be(-1)
}
"Buffered Stream" should "read data once into buffer when bufferSize is larger than number of bytes available" in {
val str = new MyBufferedInputStream(newInputStream("abc"), 4) with ConsoleLogger with TimestampLogger
str.readBuffered() should be('a')
str.readBuffered() should be('b')
str.readBuffered() should be('c')
str.readBuffered() should be(-1)
}
private def newInputStream(str: String) = {
new ByteArrayInputStream(str.getBytes(UTF_8))
}
}
编辑:
使用预初始化字段实现 class 的语法是
class ImplementingClass extends {val field1 = ???; val field2 = ???} with AbstractSuperclass with Trait1 with Trait2
请注意,尽管有关键字 with
,(抽象)superclass 需要位于第一个位置,并且您不能扩展多个 class。
在示例中,
class MyBufferedInputStream(val input: InputStream, size: Int) extends {val bufferSize: Int = size} with InputStream with Buffered
有效,但在这种情况下使 bufferSize
成为构造函数参数更具可读性
class MyBufferedInputStream(val input: InputStream, val bufferSize: Int) extends InputStream with Buffered
How do I specify an early definition for
val bufferSize
in classMyBufferedInputStream
? All examples online show early definition with a single trait, not with multiple classes and traits.
我认为早期定义 - 或 Chapter 20.5 in Programming in Scala, First Edition 中所谓的 "Pre-initialized fields" - 基于你混合的特征数量没有区别。但是让我们通过示例:
让我们假设您没有在 Buffered
中初始化 bufferSize
,即
trait Buffered {
self: InputStream =>
val bufferSize: Int
val buff = new Array[Byte](bufferSize)
/* ... */
}
首先,实现缓冲输入流的错误方法:
class WrongBufferedInputStream(val input: InputStream) extends InputStream with Buffered {
val bufferSize = 1024
override def read() = input.read()
}
因为superclass的字段,即Buffered.buff
在subclass的字段之前被初始化,所以new Array[Byte](bufferSize)
在[=22=之前被调用] 被初始化,因此当 bufferSize
是 0
时,导致长度为零的缓冲区。
这个问题有多种解决方案:
- make
bufferSize
在实现 class alazy val
所以它在第一次使用时被初始化 - 使用预初始化字段
- 使
bufferSize
成为构造函数参数
代码:
class MyBufferedInputStreamLazyInitialization(val input: InputStream) extends InputStream with Buffered {
lazy val bufferSize = 1024
override def read() = input.read()
}
class MyBufferedInputStream(val input: InputStream) extends {val bufferSize: Int = 1024} with InputStream with Buffered {
override def read() = input.read()
}
class MyBufferedInputStreamWithCustomSize(val input: InputStream, val bufferSize: Int = 1024) extends InputStream with Buffered {
override def read() = input.read()
}
用法示例:
println(new WrongBufferedInputStream(System.in).buff.length) // 0
println(new MyBufferedInputStreamLazyInitialization(System.in).buff.length) // 1024
println(new MyBufferedInputStream(System.in).buff.length) // 1024
println(new MyBufferedInputStreamWithCustomSize(System.in).buff.length) // 1024
println(new MyBufferedInputStreamWithCustomSize(System.in, 512).buff.length) // 512
Does this look like a correct approach to what the question is asking for?
我没有 Scala for the Impatient 的副本,但我觉得这个方法不错。
多亏了自我类型,你可以注入任何 InputStream
并创建,例如,缓冲文件输入流:
class BufferedFileInputStream(file: File) extends {val bufferSize = 1024} with FileInputStream(file) with Buffered
您还可以创建 InputStream
的其他装饰器并将它们混合在一起,例如
trait Awesomeness {
self: InputStream =>
/* add some awesome functionality */
}
class BufferedAwesomeInputStream(val input: InputStream) extends {val bufferSize = 1024} with InputStream with Buffered with Awesomeness {
override def read() = input.read()
}
虽然方法看起来不错,但具体实现有点奇怪。例如,readBuffered
returns 要么是当前位置的缓冲值,要么是缓冲区中存储的字节数,巧合的是,它们都可以看作是一个 Int
,但在概念上是不同的.此外,在 public 接口中公开 var
字段和可变数组 buff
是危险的,因为这使客户端代码能够改变 Buffered
的内部状态。
我理解 InputStream.read()
return 是 "code" -1
如果到达流的末尾。然而,如果缓冲区中没有可用数据,更惯用的 readBuffered()
实现将 return 和 Option[Byte]
、returning None
:
def readBuffered(): Option[Byte] = {
if (isAvailableInBuffer) {
Some(incrementAndGet())
} else {
count = fill()
if (isAvailableInBuffer) Some(incrementAndGet()) else None
}
}
用法示例:
val f = new MyBufferedInputStream(new InputStream {
val data = "hello" map (_.toByte)
var pos = 0
override def read(): Int = if (pos < data.length){pos = pos + 1; data(pos - 1).asInstanceOf[Int]} else -1
}, 1024)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(h)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(e)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(l)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(l)
println(f.readBuffered() map (_.asInstanceOf[Char])) // Some(o)
println(f.readBuffered() map (_.asInstanceOf[Char])) // None