《快学Scala》- 练习题 第十章 特质

      • 问题一:定义特质,与类java.awt.geom.Ellipse2D混入
      • 问题二:定义特质scala.math.Ordered[Point]
      • 问题三:查看BitSet类的线性化规格说明
      • 问题四:定义特质scala.math.Ordered[Point]
      • 问题五: Java Beans 规范属性变更监听器(property change listener)
      • 问题六:Java AWT类库中,Container类
      • 问题七:设计Scala特质
      • 问题八:设计特质完成java.io.BufferedInputStream修饰器的作用
      • 问题九:使用本章的日志生成器特质,给前一个练习中的方案添加日志功能,要求体现出缓冲的效果。
      • 问题十:实现一个IterableInputStream类,扩展java.io.InputStream并混入Iterable[Byte]特质

问题一:定义特质,与类java.awt.geom.Ellipse2D混入

// 问题一:
// java.awt.Rectangle类有2个很有用的方法translate和grow, 
// 但可惜的是像java.awt.geom.Ellipse2D这样的类中没有,
// 在scala中, 你可以解决掉这个问题, 定义一个RectangleLike的特质, 假如具体的translate和grow方法, 
// 提供任何你需要用来实现的抽象方法, 以便你可以像如下代码这样混入该特质

val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30 ) with RectangleLike
egg.translate(10, -10)
egg.grow(10, 20)

trait RectangleLike{
   //自身类型,表明该特质只能混入java.awt.geom.Ellipse2D.Double的子类
    this :  java.awt.geom.Ellipse2D.Double =>   
    translate(x: Double, y: Double){
        ...   //
    }

    grow(x: Double, y: Double){
        ...
    }
}

问题二:定义特质scala.math.Ordered[Point]

import java.awt.Point

/*
 * 问题二:
 * 通过把scala.math.Ordered[Point]混入java.awt.Point的方式, 定义OrderedPoint类,
 * 按辞典方式排序, 也就是说, 如果x < x' 或者x = x'且 y < y' 则 (x,y) < (x', y')则(x,y) < (x',y')。
 */
class OrderedPoint extends java.awt.Point with scala.math.Ordered[Point] {
  override def compare(that: Point): Int = {
    if ((this.x < that.x) || (this.x == that.x && this.y < that.y)) -1
    else if(this.x == that.x && this.y == that.y) 0
    else 1
  }
}

问题三:查看BitSet类的线性化规格说明

  /*
   * 10.3
   * 查看BitSet类,将它的所有超类和特质绘制成一张图, 忽略类型参数, 然后给出该特质的线性化规格说明
   * 略,等待补充
   */

问题四:定义特质scala.math.Ordered[Point]

  /*
   * 10.4
   * 提供一个CryptoLogger类, 将日志消息以凯撒密码加密, 缺省情况下密钥为3, 不过使用者也可以重写它, 提供缺省密钥和-3作为密钥时的使用示例
   */
package example.traits.exercise

trait Logger {
  val key = 3

  def log(message: String): String
}

// 凯撒密码加密
class CryptoLogger extends Logger {
  override def log(message: String): String = {
    for( ch <- message) yield if(key >= 0) (97 + (ch - 97 + key) % 26).toChar else (97 + (ch - 97 + key + 26 ) % 26).toChar
  }
}


//Test
class TraitTest extends FunSuite with BeforeAndAfter {

  before {
    println("---------")
  }
  test("Trait-Exercise-CryptoLogger") {
    var logger = new CryptoLogger
    var result = logger.log("abc")
    println(result)
    assert(result === "def")

    logger = new CryptoLogger {
      override val key: Int = -3
    }
    result = logger.log("abc")
    println(result)
    assert(result === "xyz")
  }
}

问题五: Java Beans 规范属性变更监听器(property change listener)

/*  问题五:
   * JavaBeans规范里有一种提法叫做属性变更监听器(property change listener),
   * 这是bean用来通知其属性变更的标准方式, 
   * PropertyChangeSupport类对于任何想要支持属性变更监听器的bean而言是个便捷的超类,
   * 但可惜已有其他超类的类, 比如JComponent , 必须重新实现相应的方法, 
   * 将PropertyChangeSupport重新实现为一个特质, 然后把它混入到java.awt.Point类中
   * */
import java.beans.{PropertyChangeEvent, PropertyChangeListener}


trait PropertyChangeSupportLike { 

  lazy val pcs = new java.beans.PropertyChangeSupport(this)   

  def addPropertyChangeListener(listener: PropertyChangeListener): Unit = pcs.addPropertyChangeListener(listener)

  def removePropertyChangeListener(listener: PropertyChangeListener): Unit = pcs.removePropertyChangeListener(listener)

  def getPropertyChangeListeners: Array[PropertyChangeListener] = pcs.getPropertyChangeListeners

  def addPropertyChangeListener(propertyName: String, listener: PropertyChangeListener): Unit = pcs.addPropertyChangeListener(propertyName, listener)

  def removePropertyChangeListener(propertyName: String, listener: PropertyChangeListener): Unit = pcs.removePropertyChangeListener(propertyName, listener)

  def getPropertyChangeListeners(propertyName: String): Array[PropertyChangeListener] = pcs.getPropertyChangeListeners(propertyName)

  def firePropertyChange(propertyName: String, oldValue: Object, newValue: Object): Unit = pcs.firePropertyChange(propertyName, oldValue, newValue)

  def firePropertyChange(propertyName: String, oldValue: Int, newValue: Int): Unit = pcs.firePropertyChange(propertyName, oldValue, newValue)

  def firePropertyChange(propertyName: String, oldValue: Boolean, newValue: Boolean): Unit = pcs.firePropertyChange(propertyName, oldValue, newValue)

  def firePropertyChange(event: PropertyChangeEvent): Unit = pcs.firePropertyChange(event)

  def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Object, newValue: Object): Unit = fireIndexedPropertyChange(propertyName, index, oldValue, newValue)

  def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Int, newValue: Int): Unit = fireIndexedPropertyChange(propertyName, index, oldValue, newValue)

  def fireIndexedPropertyChange(propertyName: String, index: Int, oldValue: Boolean, newValue: Boolean): Unit = fireIndexedPropertyChange(propertyName, index, oldValue, newValue)

  def hasListeners(propertyName: String): Boolean = pcs.hasListeners(propertyName)

}

//Test
class TraitTest extends FunSuite with BeforeAndAfter {

  before {
    println("---------")
  }
  test("Trait-Exercise-PropertyChangeSupport") {
    val point = new java.awt.Point(2, 3) with PropertyChangeSupportLike
    point.addPropertyChangeListener("x", (evt: PropertyChangeEvent) => {
      println(evt)
    })

    point.x = 3
    point.firePropertyChange("x", 2, 3)

  }
}

问题六:Java AWT类库中,Container类

  /*
   * 问题六:
   * 在Java AWT类库中, 我们有一个Container类, 一个可以用于各种组件的Component子类, 举例来说,
   * Button是一个Component, 但Panel是Container, 这是一个运转中的组合模式,
   * Swaing有JComponent和JContainer,但如果你仔细看的话,你会发现一些奇怪的细节,
   * 尽管把其他组件添加到比如JButton中毫无意义, JComponent依然扩展自Container,
   * Swing的设计者们理想情况下应该会更倾向于图10-4中的设计,
   * 但在Java中那是不可能的, 请解释这是为什么?Scala中如何用特质来设计出这样的效果呢?
   */
// 答:Java语言、Scala语言,仅仅单继承。JContainer不能同时继承JComponent和Container

//使用trait
trait Componet {}
trait JComponet extends Componet{}
class JButton extends JComponet{}

trait Container extends Component{}
trait JContainer extends JComponent with Container{}
class JPanel extends JContainer{}

问题七:设计Scala特质

问题八:设计特质完成java.io.BufferedInputStream修饰器的作用

// 问题八:
// 在java.io类库中,你可以通过BufferedInputStream修饰器来给输入流增加缓冲机制。
// 用特质来重新实现缓冲。简单起见, 重写read方法


import java.io.{IOException, InputStream}

trait BufferedInputStreamLike {
  this: InputStream =>

  protected val bufSize: Int = BufferedInputStreamLike.DEFAULT_BUFFER_SIZE
  protected var pos: Int = 0
  protected var count: Int = 0

  protected val buf = new Array[Byte](bufSize)

  override def read(): Int = synchronized{
    if (pos >= count) {
      fill()
      if (pos >= count)
         return -1
    }
    val buffer = getBufIfOpen
    pos += 1
    buffer(pos - 1)
  }

  private def fill(): Unit = {
    count = 0
    pos = 0
    val n = this.read(getBufIfOpen, 0, buf.length)
    if (n > 0)
      count += n
  }


  private def getBufIfOpen: Array[Byte] = {
    if (buf == null) new IOException("Stream closed")
    buf
  }

}

object BufferedInputStreamLike {
  val DEFAULT_BUFFER_SIZE = 8192
}

class TraitTest extends FunSuite with BeforeAndAfter {

  before {
    println("---------")
  }

  test("BufferedInputStreamLike") {
    var start = System.nanoTime()

    val fileInputStream = new FileInputStream("/home/jack/top")
    for (i <- 1 to 100000) fileInputStream.read()

    var end = System.nanoTime()
    println(s"Without\tBufferedInputStreamLike, cost time:${end -start} ns" )
    fileInputStream.close()



    // Part II
    start = System.nanoTime()

    val bufferedInputStream: FileInputStream with BufferedInputStreamLike = new {val bufferSize = 100000} with FileInputStream("/home/jack/top") with BufferedInputStreamLike
    for (i <- 1 to 100000) bufferedInputStream.read()

    end = System.nanoTime()
    println(s"With\tBufferedInputStreamLike, cost time:${end -start} ns" )

    bufferedInputStream.close()

  }

// sbt shell 
// testOnly Trai* -- -z "InputStreamLike"
/*
---------
Without BufferedInputStreamLike, cost time:54517999 ns
With    BufferedInputStreamLike, cost time:19849703 ns
[info] TraitTest:
[info] - BufferedInputStreamLike
[info] Run completed in 191 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 0 s, completed Aug 2, 2018 5:01:07 PM
*/

问题九:使用本章的日志生成器特质,给前一个练习中的方案添加日志功能,要求体现出缓冲的效果。

package example.traits

trait Logger { 
  def log(msg: String): Unit ={}
}

package example.traits

trait ConsoleLogger extends Logger {
  override def log(msg: String): Unit = {
    println(msg)
  }
}
package example.traits.exercise

import java.io.{IOException, InputStream}

trait BufferedInputStreamLike {
  /** trait指定自类型 **/
  this: InputStream with example.traits.Logger =>    

  protected val bufSize: Int = BufferedInputStreamLike.DEFAULT_BUFFER_SIZE
  protected var pos: Int = 0
  protected var count: Int = 0

  protected val buf = new Array[Byte](bufSize)

  override def read(): Int = synchronized {
    if (pos >= count) {
      fill()
      if (pos >= count)
        return -1
    }
    val buffer = getBufIfOpen
    pos += 1
    buffer(pos - 1)
  }

  private def fill(): Unit = {
    count = 0
    pos = 0
    val n = this.read(getBufIfOpen, 0, buf.length)
    if (n > 0) {
      count += n
      log("buffered %d bytes: %s".format(n, buf.mkString(", ")))    /**添加日志功能**/
    }
  }


  private def getBufIfOpen: Array[Byte] = {
    if (buf == null) new IOException("Stream closed")
    buf
  }

}

object BufferedInputStreamLike {
  val DEFAULT_BUFFER_SIZE = 8192
}

  test("BufferedInputStreamLike - ConsoleLogger") {
    // Part II
    val start = System.nanoTime()

    val bufferedInputStream = new {
      override val bufSize = 20
    } with FileInputStream("/home/jack/top") with BufferedInputStreamLike with ConsoleLogger
    for (i <- 1 to 10) bufferedInputStream.read()

    val end = System.nanoTime()
    println(s"With\tBufferedInputStreamLike, cost time:${end - start} ns")

    bufferedInputStream.close()
  }

  /**  sbt shell  日志 
[IJ]sbt:scala-tutorial> testOnly Trai* -- -z "ConsoleLogger"
---------
buffered 20 bytes: 85, 83, 69, 82, 32, 32, 32, 32, 32, 32, 32, 80, 73, 68, 32, 37, 67, 80, 85, 32
With    BufferedInputStreamLike, cost time:774421 ns
[info] TraitTest:
[info] - BufferedInputStreamLike - ConsoleLogger
[info] Run completed in 107 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 0 s, completed Aug 2, 2018 5:32:07 PM
  */

问题十:实现一个IterableInputStream类,扩展java.io.InputStream并混入Iterable[Byte]特质

package example.traits.exercise

// java.io.FileInputStream 存在native方法: 
// private native int read0() throws IOException;
// private native int readBytes(byte b[], int off, int len) throws IOException;
// private native int available0() throws IOException;
// SocketInputStream也存在类似native方法

// 但是本例中IterableInputStream,缺少相关的native方法。
// 本题,仅是了解概念,IterableInputStream 无法实例化。
abstract class IterableInputStream extends java.io.InputStream with Iterable[Byte] {

  class IterableInputStreamIterator(inputStream: IterableInputStream) extends Iterator[Byte] {

    override def hasNext: Boolean = inputStream.available() > 0

    override def next(): Byte = inputStream.read().toByte
  }


  override def iterator: Iterator[Byte] = new IterableInputStreamIterator(this)

}

Github 答案参考
《快学scala》习题解答-第十章-特质

你可能感兴趣的:(Scala学习)