• Scala实践12


    1、内部类和抽象类型成员作为对象成员

    •  内部类

            在Scala中,可以让类将其他类作为成员。这些内部类是封闭类的成员。在Scala中,这样的内部类绑定到外部对象。假设希望编译器在编译时阻止我们混合哪些节点属于哪个图。路径相关类型提供了解决方案。

    为了说明差异,绘制了图数据类型的实现:

    class Graph {
      class Node {
        var connectedNodes: List[Node] = Nil
        def connectTo(node: Node) {
          if (connectedNodes.find(node.equals).isEmpty) {
            connectedNodes = node :: connectedNodes
          }
        }
      }
      var nodes: List[Node] = Nil
      def newNode: Node = {
        val res = new Node
        nodes = res :: nodes
        res
      }
    }
    

      上例将图表表示为节点列表(List[Node])。每个节点都有一个与其连接的其他节点的列表(connectedNodes)。这class Node是一个路径依赖类型,因为它嵌套在class Graph。因此,connectedNodes必须使用newNode来自同一实例的所有节点创建Graph

    val graph1: Graph = new Graph
    val node1: graph1.Node = graph1.newNode
    val node2: graph1.Node = graph1.newNode
    val node3: graph1.Node = graph1.newNode
    node1.connectTo(node2)  
    node3.connectTo(node1)

            明确宣布的类型node1node2以及node3作为graph1.Node为清楚起见,但编译器可能推断出它。这是因为当我们调用graph1.newNode哪些调用时new Node,该方法正在使用Node特定于实例的实例graph1

    如果现在有两个图形,Scala的类型系统不允许我们将一个图形中定义的节点与另一个图形的节点混合,因为另一个图形的节点具有不同的类型。这是一个非法的程序:

    val graph1: Graph = new Graph
    val node1: graph1.Node = graph1.newNode
    val node2: graph1.Node = graph1.newNode
    node1.connectTo(node2)      // 合法的
    val graph2: Graph = new Graph
    val node3: graph2.Node = graph2.newNode
    node1.connectTo(node3)      // 非法的
    

      graph1.Nodegraph2.Node不同图表。在Scala中,这样的类型可以表示,它是写的Graph#Node。如果希望能够连接不同图形的节点,必须通过以下方式更改初始图形实现的定义:

    class Graph {
      class Node {
        var connectedNodes: List[Graph#Node] = Nil
        def connectTo(node: Graph#Node) {      //Graph#Node与上面匹配
          if (connectedNodes.find(node.equals).isEmpty) {
            connectedNodes = node :: connectedNodes
          }
        }
      }
      var nodes: List[Node] = Nil
      def newNode: Node = {
        val res = new Node
        nodes = res :: nodes
        res
      }
    }
    

      

    • 抽象类型

             抽象类型(如traits和抽象类)又可以具有抽象类型成员。这意味着具体实现定义了实际类型。这是一个例子:

    trait Buffer {
      type T
      val element: T
    }
    

      这里定义了一个摘要type T。它用于描述类型element,并使之更具体。

    abstract class SeqBuffer extends Buffer {
      type U
      type T <: Seq[U]
      def length = element.length
    }
    

      注意如何U在上类型绑定的规范中使用另一个抽象类型T。这class SeqBuffer允许我们仅通过声明类型T必须Seq[U]是新抽象类型的子类型来仅在缓冲区中存储序列U

    具有抽象类型成员的特征或类通常与匿名类实例一起使用。为了说明这一点,我们现在看一个程序,它处理一个引用整数列表的序列缓冲区:

    abstract class IntSeqBuffer extends SeqBuffer {
      type U = Int
    }
    
    
    def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =
      new IntSeqBuffer {
           type T = List[U]
           val element = List(elem1, elem2)
         }
    val buf = newIntSeqBuf(7, 8)
    println("length = " + buf.length)
    println("content = " + buf.element)
    

      这里工厂newIntSeqBuf使用IntSeqBuf(ie new IntSeqBuffer)的匿名类实现将抽象类型T设置为具体类型List[Int]

    也可以将抽象类型成员转换为类的类型参数,反之亦然。这是上面代码的一个版本,它只使用类型参数:

    abstract class Buffer[+T] {
      val element: T
    }
    abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] {
      def length = element.length
    }
    
    def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] =
      new SeqBuffer[Int, List[Int]] {
        val element = List(e1, e2)
      }
    
    val buf = newIntSeqBuf(7, 8)
    println("length = " + buf.length)
    println("content = " + buf.element)
    

      

    2、复合类型

          有时需要表达对象的类型是其他几种类型的子类型。在Scala中,这可以在复合类型的帮助下表达,复合类型是对象类型的交叉点。

    假设我们有两个特点CloneableResetable

    trait Cloneable extends java.lang.Cloneable {
      override def clone(): Cloneable = {
        super.clone().asInstanceOf[Cloneable]
      }
    }
    trait Resetable {
      def reset: Unit
    }
    

      现在假设我们想编写一个cloneAndReset接受对象的函数,克隆它并重置原始对象:

    def cloneAndReset(obj: ?): Cloneable = {
      val cloned = obj.clone()
      obj.reset
      cloned
    }
    

      问题出现了参数的类型obj。如果是,Cloneable则对象可以是cloned,但不是reset; 如果它是Resetable我们可以reset,但没有clone操作。为了避免这种情况的类型转换,我们可以指定类型objCloneableResetable。这种复合类型在Scala中是这样写的:Cloneable with Resetable

    更新的功能如下:

    def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {
      //...
    }
    

      复合类型可以由多个对象类型组成,它们可以具有单个细化,可以用于缩小现有对象成员的签名。一般形式是:A with B with C ... { refinement }

    3、自我类型

    自我类型是一种声明特征必须混合到另一个特征中的方法,即使它没有直接扩展它。这使得依赖的成员可以在没有导入的情况下使用。

    自我类型是一种缩小this别名的类型或另一个标识符的方法this。语法看起来像普通函数语法,但意味着完全不同的东西。

    要在特征中使用自我类型,请写入标识符,要混合的另一个特征的类型,以及=>(例如someIdentifier: SomeOtherTrait =>)。

    trait User {
      def username: String
    }
    
    trait Tweeter {
      this: User =>  
      def tweet(tweetText: String) = println(s"$username: $tweetText")
    }
    
    class VerifiedTweeter(val username_ : String) extends Tweeter with User { 
    	def username = s"real $username_"
    }
    
    val realBeyoncé = new VerifiedTweeter("Beyoncé")
    realBeyoncé.tweet("Just spilled my glass of lemonade")
    

      因为this: User =>trait Tweeter,现在的变量username是在范围上的tweet方法。这也意味着,自VerifiedTweeter扩展以来Tweeter,它还必须混合User(使用with User)。

     

  • 相关阅读:
    (3)合并列值与分拆列值
    (2)SQL语句实现表的横向聚合
    (1)显示每个类别最新更新的数据
    【实践】WCF传输安全2:基于SSL的WCF匿名客户端
    超经典解释什么叫网关
    List集合操作一:遍历与查找
    RGB值及中文名称
    绑定树控件
    treeview的checkbox展开节点
    winform AutoScaleMode属性
  • 原文地址:https://www.cnblogs.com/0205gt/p/11039546.html
Copyright © 2020-2023  润新知