Scala特性,超类和早期定义语法

时间:2015-05-26 03:17:57

标签: scala inheritance buffer inputstream traits

我通过本书的练习来学习Scala" Scala for the Impatient"。一个练习要求:

  

Armor shield = new Armor(); shield.setDisplayName("Wooden Shield"); shield.setFireproof(false); shield.setRupees(50); 库中,使用a将缓冲添加到输入流   java.io装饰者。重新实现缓冲作为特征。对于   简单,覆盖BufferedInputStream方法。

即使我不清楚(对我来说)我应该覆盖哪个read方法(在新创建的特征或输入流中),我想出了以下内容:

read

问题:

  1. 如何在课程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() } } 中指定val bufferSize的早期定义?所有在线示例都显示了具有单一特征的早期定义,而不是具有多个类和特征。
  2. 这看起来像问题所要求的正确方法吗?
  3. 修改 根据Kulu Limpa的回答,我修改了我的代码。以下是有效的(忽略日志):

    MyBufferedInputStream

    测试:

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

1 个答案:

答案 0 :(得分:1)

修改

实现具有预初始化字段的类的语法是

class ImplementingClass extends {val field1 = ???; val field2 = ???} with AbstractSuperclass with Trait1 with Trait2

请注意,尽管有关键字with,但(摘要)超类需要位于第一个位置,并且不能扩展多个类。

在示例中,

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
  

如何在课程val bufferSize中指定MyBufferedInputStream的早期定义?在线的所有示例都显示了具有单一特征的早期定义,而不是具有多个类和特征。

我认为在早期定义中没有区别 - 或者在Chapter 20.5 in Programming in Scala, First Edition中调用“预初始化字段” - 基于您混合的特征数量。但让我们看一下示例:

我们假设您未在bufferSize中初始化Buffered,即

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

因为超类的字段,即Buffered.buff在子类的字段之前被初始化,所以在new Array[Byte](bufferSize)被初始化之前调用bufferSize,因此bufferSize0,导致长度为零的缓冲区。

这个问题有多种解决方案:

  • 在实施类bufferSize中生成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
  

这看起来像问题所要求的正确方法吗?

我没有 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返回当前位置的缓冲值,或缓冲区中存储的字节数,巧合的是,它们都可以看作是Int,但在概念上是不同的。此外,在公共接口中公开var字段和可变数组buff是危险的,因为这使客户端代码能够改变Buffered的内部状态。

我了解如果到达流的末尾,InputStream.read()会返回“代码”-1。然而,如果缓冲区中没有可用数据,则更惯用的readBuffered()实现将返回Option[Byte],返回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