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)

明确宣布的类型node1,node2以及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.Node与graph2.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中,这可以在复合类型的帮助下表达,复合类型是对象类型的交叉点。

假设我们有两个特点Cloneable和Resetable:

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操作。为了避免这种情况的类型转换,我们可以指定类型obj既Cloneable和Resetable。这种复合类型在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)。

Scala实践12的更多相关文章

  1. [翻译]The Neophyte's Guide to Scala Part 12: Type Classes

    The Neophyte's Guide to Scala Part 12: Type Classes 过去的两周我们讨论了一些使我们保持DRY和灵活性的函数式编程技术,特别是函数组合,partial ...

  2. 【原创 Hadoop&Spark 动手实践 12】Spark MLLib 基础、应用与信用卡欺诈检测系统动手实践

    [原创 Hadoop&Spark 动手实践 12]Spark MLLib 基础.应用与信用卡欺诈检测系统动手实践

  3. Scala实践4

    一.数组 在Scala中,用()来访问元素,数组声明的语法格式如下 : var z:Array[String] = new Array[String](3) 或 var z = new Array[S ...

  4. Scala实践5

    一.Scala的层级 1.1类层级 Scala中,Any是所其他类的超类,在底端定义了一些有趣的类NULL和Nothing,是所有其他类的子类. 根类Any有两个子类:AnyVal和AnyRef.其中 ...

  5. Socket编程实践(12) --UDP编程基础

    UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...

  6. 《高级软件测试》web测试实践--12月31日记录

    今日的任务进度如上图所示.我们对华科软件学院和计算机学院的网站进行了对比分析,分析的角度包括基本功能分析.前端性能分析.用户调研等.在这里我们简单总结下我们得到的评测结果. 基本功能分析:计算机学院和 ...

  7. 《高级软件测试》web测试实践--12月30日记录

    考完数学,我们正式开始web测试实践的作业,今天,我们主要进行了方案的选择和人员的分工.任务计划和安排如上图所示. 任务进展:完成题目选择和人员分工: 遇到问题:暂无: 下一步任务:完成软件评测.用户 ...

  8. Linux IPC实践(12) --System V信号量(2)

    实践1:信号量实现进程互斥 父子进程执行流程如下: 父进程 子进程 P P O(print) X(print) sleep sleep O(print) X(print) V V sleep slee ...

  9. es6+最佳入门实践(12)

    12.class基础用法和继承 12.1.class基础语法 在es5中,面向对象我们通常写成这样 function Person(name,age) { this.name = name; this ...

随机推荐

  1. HDU 1114 完全背包问题

    题意:有一个存钱罐,空罐时的重量是e,满罐时的重量是f,现在有n种硬币,每一种有无限个,现在给出每一种硬币的价值p和重量w,问存钱罐中最少钱,输出最小钱,否则输出... 思路:变形的完全背包问题,只是 ...

  2. Python--day41--条件

    1,条件 #锁 #acquire release#一个条件被创建之初 默认有一个False状态#False状态 会影响wait一直处于等待状态#notify(int数据类型) 造钥匙 代码示例:条件. ...

  3. Python--day37--守护进程和几个常用的方法

    1,p.daemon = True #设置子进程为守护进程 #守护进程会随着主进程的代码执行完毕 而结束 #子进程 --> 守护进程 import time from multiprocessi ...

  4. 2019-8-4-自动更新所有-Git-仓库

    title author date CreateTime categories 自动更新所有 Git 仓库 lindexi 2019-08-04 14:44:59 +0800 2019-08-01 2 ...

  5. Ultra-QuickSort——[归并排序、分治求逆序对]

    Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...

  6. 2019-9-2-win10-uwp-列表模板选择器

    title author date CreateTime categories win10 uwp 列表模板选择器 lindexi 2019-09-02 12:57:38 +0800 2018-2-1 ...

  7. CSS兼容性问题的解决方式(更新中···)

    1.清除浮动的兼容性(低版本的浏览器不兼容问题) .clearfix:after{ content:""; clear:both; display:block; visibilit ...

  8. c++ unordered_map 自定义key

    C++11新增了一类散列容器包括unordered_set, unordered_map, unordered_multiset, unordered_multimap, 即之前熟悉的hash_set ...

  9. 【题解】P5589 小猪佩奇玩游戏(期望)

    [题解]P5589 小猪佩奇玩游戏(期望) 假设一个点有\(x\)个点(包括自己)可以到达他,他就对答案有\(1/x\)的贡献.这是因为这个点必须被删掉而通过删掉这个点本身删掉这个点的概率是\(1/x ...

  10. [**P2766** 最长不下降子序列问题](https://www.luogu.org/problemnew/show/P2766)

    P2766 最长不下降子序列问题 考虑我们是如何\(dp\)这个\(LIS\)的. 我们是倒着推,设置\(dp(i)\)代表以\(i\)为起点的\(LIS\)是多少.转移太显然了 \[ dp(i)=m ...