Scala的自定义类型标记
Scala的自定义类型标记
Scala中有很多千奇百怪的符号标记,看起来是那么的独特,就像是一杯dry martini…好像黑夜中的萤火虫,那么耀眼,那么出众。
好了言归正传,这一篇文章我们会讲一下Scala中的自定义类型标记,通过自定义类型标记可以将this指向额外的类型期望。
我们先看一个观察者模式的例子:
abstract class SubjectObserver {
type S <: Subject // <1>
type O <: Observer // <2>
trait Subject {
private var observers = List[O]() // <3>
def addObserver(observer: O) = observers ::= observer
def notifyObservers() = observers.foreach(_.receiveUpdate(this)) // <4>
}
trait Observer {
def receiveUpdate(subject: S) // <5>
}
}
分析下上面的例子,我们在一个类中同时定义了Subject和Observer, 因为Subject和Observer是trait,而不是一个世纪的类型,所以我们又定义了Subject和Object为S和O的类型上界,这就意味着S和O分别是Subject和Object的子类型。
在4的位置,notifyObservers需要通知存储在Subject中的observers,调用Observer的receiveUpdate方法。
receiveUpdate需要接受一个具体的子类型S,但是4的位置receiveUpdate(this)中传递的参数是this即Subject,这样会导致编译失败。
那么如果我们想实现在Subject中传递S类型的实例怎么办?这时候就可以使用到自定义类型标记了。
我们看下面改造的例子:
abstract class SubjectObserver {
type S <: Subject
type O <: Observer
trait Subject {
self: S => // <1>
private var observers = List[O]()
def addObserver(observer: O) = observers ::= observer
def notifyObservers() = observers.foreach(_.receiveUpdate(self)) // <2>
}
trait Observer {
def receiveUpdate(subject: S): Unit
}
}
变化的点在1和2,位置1定义了一个自定义类型标记,它说明了两个意思:
- self指向了this
- self是S类型的实例
在2中,我们直接传入self就行了,这里self也可以换做其他的字面量。
下面我们看下怎么使用这个类:
case class Button(label: String) {
def click(): Unit = {} // <1>
}
object ButtonSubjectObserver extends SubjectObserver {
type S = ObservableButton // <2>
type O = Observer
class ObservableButton(label: String) extends Button(label) with Subject {
override def click() = {
super.click()
notifyObservers()
}
}
}
import ButtonSubjectObserver._
class ButtonClickObserver extends Observer {
val clicks = new scala.collection.mutable.HashMap[String,Int]() // <3>
def receiveUpdate(button: ObservableButton): Unit = {
val count = clicks.getOrElse(button.label, 0) + 1
clicks.update(button.label, count)
}
}
我们需要定义一个Object继承SubjectObserver, 并在它的内部再定义两个class实现相应的trait。
看下我们如何给S和O赋值:
type S = ObservableButton // <2>
type O = Observer
现在一个观察者模式就完成了。这个例子中我们使用自类型标记来解决使用抽象类型成员时带来的问题。
下面我们再举一个更复杂一点的例子:
trait Persistence { def startPersistence(): Unit } // <1>
trait Midtier { def startMidtier(): Unit }
trait UI { def startUI(): Unit }
trait Database extends Persistence { // <2>
def startPersistence(): Unit = println("Starting Database")
}
trait BizLogic extends Midtier {
def startMidtier(): Unit = println("Starting BizLogic")
}
trait WebUI extends UI {
def startUI(): Unit = println("Starting WebUI")
}
trait App { self: Persistence with Midtier with UI => // <3>
def run() = {
startPersistence()
startMidtier()
startUI()
}
}
object MyApp extends App with Database with BizLogic with WebUI // <4>
MyApp.run
我们定义了一个三层的应用程序,然后在App中调用他们。
在App中我们这样定义自定义类型:
self: Persistence with Midtier with UI =>
意思是App的实例应该是Persistence,Midtier和UI的子类型。
所以在定义App对象的时候就必须要这样定义:
object MyApp extends App with Database with BizLogic with WebUI
使用自类型标记实际上与使用继承和混入等价(除了没有定义self 以外):
trait App extends Persistence with Midtier with UI {
def run = { ... }
}
也有一些特殊情况下,自类型标记的行为不同于继承。但在实践中,这两种方法可以相互替换使用。
事实上,这两种方法表达了不同的意图。刚刚展示的基于继承的实现表明应用程序是Persistence、Midtier 和UI 的一个子类型。与此相反,自类型标记则更加明确地表示其行为的组合是通过混入实现的。
更多教程请参考 flydean的博客
Scala的自定义类型标记的更多相关文章
- CodeGen编写自定义表达式标记
CodeGen编写自定义表达式标记 CodeGen支持开发人员通过编写plug-in modules插件模块来定义自定义表达式标记的能力,以提供与这些标记相关联的逻辑.这种plug-in module ...
- 《精通C#》自定义类型转化-扩展方法-匿名类型-指针类型(11.3-11.6)
1.类型转化在C#中有很多,常用的是int类型转string等,这些都有微软给我们定义好的,我们需要的时候直接调用就是了,这是值类型中的转化,有时候我们还会需要类类型(包括结构struct)的转化,还 ...
- 第54讲:Scala中复合类型实战详解
今天学习了scala的复合类型的内容,让我们通过实战来看看代码: trait Compound_Type1trait Compound_Type2class Compound_Type extends ...
- 第53讲:Scala中结构类型实战详解
今天学习了scala的结构类型,让我们看看代码 class Structural {def open() = print("A class interface opened") } ...
- C#简单问题,不简单的原理:不能局部定义自定义类型(不含匿名类型)
今天在进行代码测试时发现,尝试在一个方法中定义一个委托,注意是定义一个委托,而不是声明一个委托变量,在编写的时候没有报错,VS也能智能提示,但在编译时却报语法不完整,缺少方括号,但实际查询并没有缺少, ...
- Struts2框架的自定义类型转换器
前言:对于java的基本数据类型及一些系统类(如Date类.集合类),Struts2提供了内置类型转换功能,但是也有一定的限制.所以就演示出自定义类型转换器 一.应用于局部类型转换器 eg.用户登录出 ...
- sruts2 自定义类型转换器
1.1.1 Struts2中自定义类型转换器:(了解) 类型转换的过程是双向的过程: JSP---->Action参数提交:String---Date. Action---->JSP ...
- 一个关于自定义类型作为HashMap的key的问题
在之前的项目需要用到以自定义类型作为HashMap的key,遇到一个问题:如果修改了已经存储在HashMap中的实例,会发生什么情况呢?用一段代码来试验: import java.util.HashM ...
- Scala 深入浅出实战经典 第54讲:Scala中复合类型实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
随机推荐
- 微信小程序mpvue-动态改变navigationBarTitleText值
通过JS动态 改变navigationBarTitleText的值 能否通过JS动态改变navigationBarTitleText的值? 方法一:可以在onLoad方法中通过wx.setNaviga ...
- Java第七天,类的继承
面向对象编程的三大特征: 封装.继承.多态 今天我们学习继承! 继承是多态的前提,如果没有继承就没有多态. 继承主要解决的问题就是共性抽取(将许多类共有的特性便作父类,这样可以较大程度的优化代码). ...
- Docker之hello world
Docker Hello World Docker 允许你在容器内运行应用程序, 使用 docker run 命令来在容器内运行一个应用程序. 输出Hello world runoob@runoob: ...
- 字典树&&AC自动机---看完大概应该懂了吧。。。。
目录 字典树 AC自动机 字典树 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计 ...
- Linux c++ vim环境搭建系列(4)——vim插件安装配置使用
4. 插件 主要是c++相关的. ~/.vimrc文件在GitHub上有:https://github.com/whuwzp/vim_config 以下内容参考: https://github.com ...
- c++容器的底层数据结构
序列式容器 vector ->底层数据结构为数组,支持快速随机访问 list ->底层数据结构为双向链表,支持快速增加和删除 deque ->底层数据结构为一个中央控制器和多个缓冲区 ...
- [总结]最近公共祖先(倍增求LCA)
目录 一.定义 二.LCA的实现流程 1. 预处理 2. 计算LCA 三.例题 例1:P3379 [模板]最近公共祖先(LCA) 四.树上差分 1. 边差分 2. 点差分 3. 例题 一.定义 给定一 ...
- 把川普射上太阳—如何用python制作小游戏
1.准备 开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装. Windows环境下打开Cmd(开始—运行—CMD),苹果系统 ...
- L20 梯度下降、随机梯度下降和小批量梯度下降
airfoil4755 下载 链接:https://pan.baidu.com/s/1YEtNjJ0_G9eeH6A6vHXhnA 提取码:dwjq 梯度下降 (Boyd & Vandenbe ...
- Rank of Tetris 杭电 拓扑排序加并查集
自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球. 为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜, ...