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 ...
随机推荐
- Unity 游戏框架搭建 2019 (二十一、二十二) 第三章简介&整理前的准备
整理前的准备 到目前为止,我们积攒了很多示例了,并且每个示例也都贯彻了最的约定和规则. 在上一篇的小结也说了一个比较新的东西:编程体验优化. 在之前我们还积攒了一个问题:代码重复问题. 我们可是忍住整 ...
- 7.Metasploit后渗透
Metasploit 高阶之后渗透 01信息收集 应用场景: 后渗透的第一步,更多地了解靶机信息,为后续攻击做准备. 02进程迁移 应用场景: 如果反弹的meterpreter会话是对方打开了一个你预 ...
- 【tensorflow2.0】张量的结构操作
张量的操作主要包括张量的结构操作和张量的数学运算. 张量结构操作诸如:张量创建,索引切片,维度变换,合并分割. 张量数学运算主要有:标量运算,向量运算,矩阵运算.另外我们会介绍张量运算的广播机制. 本 ...
- jQuery和Vue的技术优劣对比
1.精力集中. Jq偏重于对dom的操作,由它的函数就很容易看出来,$().parent().find().我们用jq的时候经常要去考虑怎么去渲染数据,怎么从视图中取到数据,操作数据前必须对dom节点 ...
- ssh 解决经常断开与记住密码功能
一.解决ssh经常自动断开问题 修改 /etc/ssh/sshd_config 其中对应项为 ClientAliveInterval 30 ClientAliveCountMax 3 表示每30秒发一 ...
- 剑指Offer系列之题16~题20
目录 16.反转链表 17.合并两个排序的链表 18.树的子结构
- scratch中如何实现面向鼠标指针的相反方向?
你可以试试设置面向鼠标指针,然后再角色进行翻转,而且要是面向反方向的话,鼠标指针是自己可以调节的,面向指针也可以的 scratch学习视频 链接:https://pan.baidu.com/s/1qX ...
- 跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
提到MySQL的事务,我相信对MySQL有了解的同学都能聊上几句,无论是面试求职,还是日常开发,MySQL的事务都跟我们息息相关. 而事务的ACID(即原子性Atomicity.一致性Consiste ...
- (js描述的)数据结构[哈希表1.1](8)
(js描述的)数据结构[哈希表1.1](8) 一.数组的缺点 1.数组进行插入操作时,效率比较低. 2.数组基于索引去查找的操作效率非常高,基于内容去查找效率很低. 3.数组进行删除操作,效率也不高. ...
- Java第二天,类的概念,属性和方法的使用
上文中我们已近说到过了,Java是一种面向对象的编程语言,对象是用类来创建的,就比如世界上有无数个父亲,但是他们都有一个共同的属性--男人.也就是说某个父亲这个对象属于男人这个类.类是Java必不可少 ...