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)
明确宣布的类型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的更多相关文章
- [翻译]The Neophyte's Guide to Scala Part 12: Type Classes
The Neophyte's Guide to Scala Part 12: Type Classes 过去的两周我们讨论了一些使我们保持DRY和灵活性的函数式编程技术,特别是函数组合,partial ...
- 【原创 Hadoop&Spark 动手实践 12】Spark MLLib 基础、应用与信用卡欺诈检测系统动手实践
[原创 Hadoop&Spark 动手实践 12]Spark MLLib 基础.应用与信用卡欺诈检测系统动手实践
- Scala实践4
一.数组 在Scala中,用()来访问元素,数组声明的语法格式如下 : var z:Array[String] = new Array[String](3) 或 var z = new Array[S ...
- Scala实践5
一.Scala的层级 1.1类层级 Scala中,Any是所其他类的超类,在底端定义了一些有趣的类NULL和Nothing,是所有其他类的子类. 根类Any有两个子类:AnyVal和AnyRef.其中 ...
- Socket编程实践(12) --UDP编程基础
UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...
- 《高级软件测试》web测试实践--12月31日记录
今日的任务进度如上图所示.我们对华科软件学院和计算机学院的网站进行了对比分析,分析的角度包括基本功能分析.前端性能分析.用户调研等.在这里我们简单总结下我们得到的评测结果. 基本功能分析:计算机学院和 ...
- 《高级软件测试》web测试实践--12月30日记录
考完数学,我们正式开始web测试实践的作业,今天,我们主要进行了方案的选择和人员的分工.任务计划和安排如上图所示. 任务进展:完成题目选择和人员分工: 遇到问题:暂无: 下一步任务:完成软件评测.用户 ...
- Linux IPC实践(12) --System V信号量(2)
实践1:信号量实现进程互斥 父子进程执行流程如下: 父进程 子进程 P P O(print) X(print) sleep sleep O(print) X(print) V V sleep slee ...
- es6+最佳入门实践(12)
12.class基础用法和继承 12.1.class基础语法 在es5中,面向对象我们通常写成这样 function Person(name,age) { this.name = name; this ...
随机推荐
- uni-app学习记录03-路由跳转
<template> <view class="content"> <!-- v-show是相对于display: none --> <v ...
- CodeForces 1243"Character Swap (Hard Version)"(multimap)
传送门 •前置知识-multimap的用法 $multimap$ 与 $map$ 的区别在于一个 $key$ 可以对应几个值: 对于 $map$ 而言,一个 $key$ 只能对应一个值,并且按照 $k ...
- Vue 用第三方的库去实现动画效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- webpack学习(一)项目中安装webpack
如何在项目中安装webpack,webpack-cli? 前提:电脑安装了 node和npm包管理工具 1 创建项目文件夹或者在已有的项目中打开终端 输入相关命令: npm init 因为已经安装好 ...
- H3C DHCP中继工作原理
- linux 快速和慢速处理
老版本的 Linux 内核尽了很大努力来区分"快速"和"慢速"中断. 快速中断是那些能够很 快处理的, 而处理慢速中断要特别地长一些. 慢速中断可能十分苛求处理 ...
- JRoll 2 适用于移动开发滚动(滑动)——轻量级插件
JRoll,一款能滚起上万条数据,具有滑动加速.回弹.缩放.滚动条.滑动事件等功能,兼容CommonJS/AMD/CMD模块规范,开源,免费的轻量级html5滚动插件. 官网:http://www.c ...
- 2018-2-13-WPF-异常-NativeWPFDLLLoader.LoadNativeWPFDLL
title author date CreateTime categories WPF 异常 NativeWPFDLLLoader.LoadNativeWPFDLL lindexi 2018-2-13 ...
- Sending Packets LightOJ - 1321 (期望计算)
题面: Alice and Bob are trying to communicate through the internet. Just assume that there are N route ...
- 新书《iOS编程(第6版)》抢鲜试读
我最近翻译了Big Nerd Ranch的<iOS编程(第6版)>.我用了大半年时间,尽可能做到通顺易懂.不足之处请大家多多指正.感谢辛苦审校的丁道骏同学. 这本书得过Jolt大奖,原书在 ...