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 ::= observerdef 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 <: Subjecttype O <: Observertrait Subject {self: S =>         // <1>                                              private var observers = List[O]()def addObserver(observer: O) = observers ::= observerdef notifyObservers() = observers.foreach(_.receiveUpdate(self)) // <2>}trait Observer {def receiveUpdate(subject: S): Unit}
}

变化的点在1和2,位置1定义了一个自定义类型标记,它说明了两个意思:

  1. self指向了this
  2. self是S类型的实例

在2中,我们直接传入self就行了,这里self也可以换做其他的字面量。

下面我们看下怎么使用这个类:

case class Button(label: String) {                                  def click(): Unit = {}   // <1>
}object ButtonSubjectObserver extends SubjectObserver {               type S = ObservableButton  // <2>type O = Observerclass 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) + 1clicks.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 的一个子类型。与此相反,自类型标记则更加明确地表示其行为的组合是通过混入实现的。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
  • Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
  • Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
  • java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程

更多教程请参考 flydean的博客

Scala的自定义类型标记相关推荐

  1. CodeGen编写自定义表达式标记

    CodeGen编写自定义表达式标记 CodeGen支持开发人员通过编写plug-in modules插件模块来定义自定义表达式标记的能力,以提供与这些标记相关联的逻辑.这种plug-in module ...

  2. Golang——Tag、自定义类型、未命名类型

    Tag: tag并不是注释,而是用来对字段进行描述的元数据.尽管它不属于数据成员, 但却是类型的组成部分. 在运行期,可用反射获取标签信息.常被用作格式校验,数据库关系映射等 Tag是一个字符串,以k ...

  3. html怎么用脚本显示隐藏,使用隐藏状态而不是注释或自定义脚本标记来模板化HTML...

    Javascript最佳做法&惯例,例如John Resig和Nicholas Zachas强调的那些,可维护JavaScript一书的作者,建议使用HTML注释或带有自定义类型的脚本标签来存 ...

  4. MyBatis核心源码剖析(SqlSession XML解析 Mapper executor SQL执行过程 自定义类型处理器 缓存 日志)

    MyBatis核心源码剖析 MyBatis核心源码剖析 1 MyBatis源码概述 1.1 为什么要看MyBatis框架的源码 1.2 如何深入学习MyBatis源码 1.3 源码分析的5大原则 2 ...

  5. Oracle type (自定义类型的使用)

    oracle - type type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要 ...

  6. Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)

    1. 自定义类型格式 用户自定义类型使用关键字 type ,其语法格式是: type newType oldType oldType 可以是自定义类型.预声明类型.未命名类型中的任意一种. newTy ...

  7. GreenDao存储自定义类型对象解决方案(转)

    最近公司项目选用GreenDao作为Android客户端本地数据库的对象关系映射框架.对于GreenDao虽然以往也有简单用过,但这还是笔者第一次在实际业务中使用.碰到了题目所述的两个问题,虽然在Tu ...

  8. vba 定义类_VBA|自定义类型、枚举类型和类模块及其使用

    VBA中,自定义类型相当于C语言中的结构体,枚举类型也与C语言中的枚举类型相似.自定义类型和枚举类型放到模块的子过程的前面即可. VBA中, 类模块相当于C语言中的类,类模板要单独放到类模块中(自定义 ...

  9. 在SQL和ERWIN中用自定义类型、规则和默认值实现check约束从而保证数据的完整性...

    ========================================================= /*创建一个名为list的规则,这条规则表明变量只能取0和1两个值*/ CREATE ...

最新文章

  1. 清北2021毕业生就业报告出炉!清华博士0人出国,70%进体制
  2. 应届生web前端面试题_2020最新Web前端经典面试题试题及答案(持续更新)
  3. bzoj 1011 近似估计
  4. 而立之年,不拼体力的我们应该关注什么?
  5. 算法训练和模型部署如何避免多次重写数据预处理代码
  6. membercache java_Java开发中的Memcache原理及实现
  7. css颜色 333是什么颜色,纯css实现的颜色扇附图
  8. JavaScript学习总结(10)——实用JS代码大全
  9. 总结之lowagie.text合并PDF文件
  10. 自动化测试,你一定要知道的知识
  11. 网络安全工程师视频教程从入门到精通学习_网络安全入门教程
  12. 人间繁华江上明月,乃浮生一梦,惟真情长在——读沈君山《浮生再记》(并转书评)
  13. 阿里巴巴图库的使用教程
  14. 前端开发笔记:记一次网站创建的过程
  15. 拼多多推广中出价是越高效果越好吗?
  16. 【报告分享】2021懂车帝经销商直播价值白皮书-懂车帝x巨量算数(附下载)
  17. 和程序员薪酬差不多的工作
  18. 用Pandas秒秒钟搞定24张Excel报表,还做了波投放分析!
  19. java freemarker 导出富文本到Word文档
  20. 面向云环境基于属性加密的密文分享系统——简介

热门文章

  1. Canvas API初步学习
  2. 应用层勾子IAT HOOK(源码)
  3. VS中MFC访问MySQL的方法
  4. Python 中的url,Base64和MD5编码解码的使用
  5. MySQL(二)InnoDB的内存结构和特性
  6. 一文讲懂什么是三层交换机、网关、DNS、子网掩码、MAC地址
  7. 再见,Navicat!这个IDEA的兄弟,真香!
  8. 面试官:说说Java中java.lang.Void和void有什么作用和区别?
  9. Shell的基本语法结构
  10. Zoe Liu:传统算法与深度学习各有所长