当前位置: 首页 > article >正文

[读书日志]从零开始学习Chisel 第十二篇:Scala的抽象成员(敏捷硬件开发语言Chisel与数字系统设计)

9. Scala的抽象成员

9.1 抽象成员

Scala有4种抽象成员,分别是抽象val字段,抽象var字段,抽象方法和抽象类型。声明如下:

scala> trait Abstract {
     | type T					    //抽象类型
     | def transform(x: T): T		 //抽象方法
     | val initial: T				//抽象val字段
     | var current: T				//抽象var字段
     | }
// defined trait Abstract

抽象类和特质不能直接使用new构造实例,只能由子类继承后实现它们。抽象类型指的是用关键字type声明的一种类型,它是某个类或特质的成员但未给出定义。

在不知道某个字段正确的值,但是明确知道在当前类的每个实例中,该字段都会有一个不可变更的值,可以使用抽象val字段。抽象val字段与无参方法类似,抽象字段保证每次使用都返回一个相同的值,抽象方法每次可能返回不一样的值。

抽象var字段和抽象val字段类似,但可以被重新赋值。

9.2 初始化抽象val字段

抽象val字段有时会承担超类参数的作用,它们允许程序员在子类中提供在超类中缺失的细节。

例如有如下特质:

trait RationalTrait {
	val numerArg: Int
	val denomArg: Int
}

要在具体的类中混入这个特质,必须实现它的两个抽象val字段:

new RationalTrait {
	val numerArg = 1
	val denomArg = 1
}

前面讲过,这不是直接实例化特质,而是隐式用一个匿名类混入了该特质。花括号中的内容属于隐式的匿名类。

在构造子类的实例对象时,首先构造超类/超特质的组件,然后才轮到子类的剩余组件。由于花括号中的内容不属于超类/超特质,所以在构造超类/超特质的组件时,花括号内的内容其实是无用的。在这个过程中,如果需要访问超类/超特制的抽象val字段,会交出相应类型的默认值,而不是花括号中的定义。只有轮到构造子类的剩余组件时,花括号的子类定义才能派上用场。例如下面这段代码:

scala> trait RationalTrait {
     | val numerArg: Int
     | val denomArg: Int
     | require(denomArg != 0)
     | }
// defined trait RationalTrait

scala> new RationalTrait {
     | val numerArg = 1
     | val denomArg = 1
     | }
java.lang.IllegalArgumentException: requirement failed
  at scala.Predef$.require(Predef.scala:324)
  at rs$line$82$RationalTrait.$init$(rs$line$82:4)
  ... 31 elided

针对这样的情况有以下两种解决方法:

9.2.1 预初始化字段

书上预初始化字段的形式是:

new { 定义 } with 超类/超特质

书上有如下示例:

scala> new {
     | val numerArg = 1
     | val denomArg = 2
     | } with RationalTrait
-- [E009] Syntax Error: ------------------------------------------------------------------------
4 |} with RationalTrait
  |  ^^^^
  |  Early definitions are not supported; use trait parameters instead
  |
  | longer explanation available when compiling with `-explain`
-- [E018] Syntax Error: ------------------------------------------------------------------------
4 |} with RationalTrait
  |                    ^
  |                    expression expected but eof found
  |
  | longer explanation available when compiling with `-explain`

实际上这个语法在Scala3中已经不再支持。这个方案不被支持,只能使用下一种方法:

9.2.2 惰性的val字段

val字段定义成惰性的,可以让程序自己确定初始化顺序,如果在val字段前加上关键字lazy,那么该字段只有在首次被使用才会进行初始化。如果是用表达式初始化,那就对表达式求值并保存,后续使用字段时都复用保存的结果而不是每次都求值表达式。

scala> trait LazyRationalTrait {
     | val numerArg: Int
     | val denomArg: Int
     | lazy val numer = numerArg / g
     | lazy val denom = denomArg / g
     | override def toString = numer + "/" + denom
     | private lazy val g = {
     | require(denomArg != 0)
     | gcd(numerArg, denomArg)
     | }
     | private def gcd(a: Int, b: Int): Int =
     | if (b == 0) a else gcd(b, a % b)
     | }
there was 1 deprecation warning; re-run with -deprecation for details
1 warning found
// defined trait LazyRationalTrait

scala> val x = 2
val x: Int = 2

scala> new LazyRationalTrait {
     | val numerArg = 1 * x
     | val denomArg = 2 * x
     | }
val res37: LazyRationalTrait = 1/2
9.3 Sacla的枚举

Scala在标准库中提供一个枚举类,scala.Enumeration,通过创建一个继承自这个类的子对象可以创建枚举。

scala> object Color extends Enumeration {
     | val Red, Green, Blue = Value
     | }
// defined object Color

Enumeration类定义了一个名为Value的内部类,以及同名的无参方法。该方法每次都返回内部类Value的全新实例,枚举对象Color的三个枚举值都分别引用了一个Value类型的实例对象。Value是内部类,所以它的对象的具体类型与外部类的实例对象有关。在这里外部类的对象就是自定义的Color,三个枚举值引用的对象的真正类型是Color.Value

方法Value有一个重载的版本,它接收一个字符串参数来给枚举值关联特定的名称。

scala> object Color extends Enumeration {
     | val Red, Green, Blue = Value
     | }
// defined object Color

scala> object Direction extends Enumeration {
     | val North = Value("N")
     | val East = Value("E")
     | val South = Value("S")
     | val West = Value("W")
     | }
// defined object Direction

scala> Color.values
val res38: Color.ValueSet = Color.ValueSet(Red, Green, Blue)

scala> Direction.values
val res39: Direction.ValueSet = Direction.ValueSet(N, E, S, W)

枚举值从0开始编号,可以通过对象名(编号)来返回对应枚举值的名称。

scala> Color(2)
val res40: Color.Value = Blue

scala> Direction(0)
val res41: Direction.Value = N

http://www.kler.cn/a/500630.html

相关文章:

  • C语言的小项目-简易计算器
  • 线程安全问题介绍
  • 依据正则表达式拦截文本
  • Elastic-Job相关
  • 【Linux】文件 文件描述符fd
  • 攻防靶场(34):隐蔽的计划任务提权 Funbox1
  • 青少年编程与数学 02-006 前端开发框架VUE 20课题、工具链
  • 39_Lua选择结构语句
  • Pathview包:整合表达谱数据可视化KEGG通路
  • 深入学习 Python 量化编程
  • Redis的各种操作
  • 【SpringBoot】@Value 没有注入预期的值
  • GPU算力平台|在GPU算力平台部署Linly-Talker 数字人对话应用教程
  • WPF基础(1.1):ComboBox的使用
  • NestJS基础教程
  • 【论文+源码】一个基于Vue.js的MOBA类游戏攻略分享平台
  • 数组(Array)和链表(Linked List)
  • docker-compose安装canal并利用rabbitmq同步多个mysql数据
  • Oracle概述
  • [离线数仓] 总结二、Hive数仓分层开发
  • Android车机DIY开发之学习篇(二)编译Kernel以正点原子为例
  • No one knows regex better than me
  • Unity连发功能实现逻辑
  • HTML-表单标签
  • 深入了解 NLTK:Python 的自然语言处理工具