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

【SpinalHDL】Scala编程之伴生对象

Scala中的伴生对象是指和在同一个文件中声明的,并且和类同名的对象。例如,下面的代码保存在名为Pizza的文件中。在Scala中,这个object Pizza 对象被认为是class Pizza 类的伴生对象:

class Pizza {
}
object Pizza {
}

这样设计的优势是伴生对象及其类可以访问彼此的私有成员(字段方法),这意味着这个类中的printFilename 方法可以工作,因为它可以访问它的伴生对象中的HiddenFilename字段:

class SomeClass {def printFilename() = {println(SomeClass.HiddenFilename)}
}
object SomeClass {private val HiddenFilename = "/tmp/foo.bar"
}

伴生对象提供的功能远不止这些,我们将在本课剩下的内容中演示它的几个最重要的功能。

不使用关键字new创建新实例

在一些Spinalhdl示例中创建某些类的新实例时不必在类名之前使用关键字new,如下面的例子所示:

val zenMasters = List(Person("Nansen"),Person("Joshu")
)

这个功能来自于伴生对象的使用。当伴生对象中定义一个方法时,它对Scala编译器有特殊的意义。Scala内置了一些语法糖,可以让你输入这样的代码: apply

val p = Person("Fred Flinstone")

在编译过程中,编译器将该代码转换为以下代码:

val p = Person.apply("Fred Flinstone")

伴生对象中的apply 方法就像一个工厂方法,Scala的语法糖允许你使用上面的语法,不用关键字就能创建新new的类实例:

启用功

为了演示这个功能是如何工作的,下面是一个Person类名和它的伴生对象中的apply方法:

class Person {var name = ""
}
object Person {def apply(name: String): Person = {var p = new Personp.name = namep}
}

为了测试这段代码,可以使用下面的技巧将类和对象同时粘贴到Scala REPL中:

  • 从命令行启动Scala REPL(使用如下命令)
  • scala 输入paste 并按[Enter]键 :
  • REPL的响应应该是这样的:
// Entering paste mode (ctrl-D to finish)
  • 现在将类和对象同时粘贴到REPL中
  • 按Ctrl-D完成“paste”过程
    当这个过程正常运行时,你应该在REPL中看到如下输出:
defined class Person
defined object Person

REPL要求使用这种技术同时输入一个类和它的伴生对象。

现在可以像这样创建一个类的新实例: Person

val p = Person.apply("Fred Flinstone")

该代码直接调用伴生对象。更重要的是还可以像这样创建一个新实例: apply

val p = Person("Fred Flinstone")

更加便利使用此功能如下

val zenMasters = List(Person("Nansen"),Person("Joshu")
)

需要说明的是,在这个过程中发生的是:

  • 输入 val p = Person(“Fred”)
  • Scala编译器会发现 new Person之前没有关键字
  • 编译器会在类的伴生对象中查找与输入的类型签名匹配的方法 apply Person
  • 如果找到方法就使用它 apply,如果没有会得到一个编译错误

创建多个构造函数

可以在一个伴生对象中创建多个方法来提供多个构造函数。下面的代码展示了如何创建一个和两个参数的构造函数。

class Person {var name: Option[String] = Nonevar age: Option[Int] = Noneoverride def toString = s"$name, $age"
}
object Person {// a one-arg constructordef apply(name: Option[String]): Person = {var p = new Personp.name = namep}// a two-arg constructordef apply(name: Option[String], age: Option[Int]): Person = {var p = new Personp.name = namep.age = agep}
}

如果像之前那样将代码粘贴到REPL中,你会看到可以像下面这样创建新实例: Person

val p1 = Person(Some("Fred"))
val p2 = Person(None)
val p3 = Person(Some("Wilma"), Some(33))
val p4 = Person(Some("Wilma"), None)

打印这些值,会得到如下结果:

val p1: Person = Some(Fred), None
val p2: Person = None, None
val p3: Person = Some(Wilma), Some(33)
val p4: Person = Some(Wilma), None

运行这样的测试时最好清除REPL的内存 :reset:paste

添加unapply方法

就像在伴生对象中添加apply方法可以构造新的对象实例一样,添加unapply方法可以反构造对象实例。我们将用一个例子来演示:
下面是一个Person类及其伴生对象的不同版本:

class Person(var name: String, var age: Int)
object Person {def unapply(p: Person): String = s"${p.name}, ${p.age}"
}

注意伴生对象定义了一个unapply方法。这个方法接受Person类型为的输入参数,并返回String。要手动测试该unapply方法首先创建一个新Person实例:

val p = new Person("Lori", 29)

然后像这样unapply测试:

val result = Person.unapply(p)

这是REPL中的unapply结果:

scala> val result = Person.unapply(p)
result: String = Lori, 29

如上所示对给定的实例进行解构。
在Scala中把一个unapply方法放在伴生对象中时,我们就说你创建了一个提取器方法,因为你已经创建了一种从Person对象中提取字段的unapply方法:

unapply 返回不同类型

在上面例子中unapply返回String a,但也可以让它返回任何东西。下面的示例返回元组中的两个字段:

class Person(var name: String, var age: Int)
object Person {def unapply(p: Person): Tuple2[String, Int] = (p.name, p.age)
}

这个方法在REPL中如下所示:

scala> val result = Person.unapply(p)
result: (String, Int) = (Lori,29)

因为这个unapply方法以元组的形式返回类字段,所以你也可以这样做:

scala> val (name, age) = Person.unapply(p)
name: String = Lori
age: Int = 29

unapply 提取器

创建提取器的一个好处是,如果你遵循正确的Scala约定,它们会在匹配表达式中启用一种方便的模式匹配形式

要点

  • 伴生对象是与A在同一个文件中声明的,并且与类 objectclass同名
  • 伴生对象及其类可以访问彼此的私有成员
  • 伴生对象的apply方法让你无需使用关键字 new就能创建类的新实例
  • 伴生对象的unapply方法允许你将一个类的实例解构为它的各个组件

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《C++移动语义:解锁复杂数据结构的高效之道》
  • 『功能项目』QFrameWork更新道具图片UGUI【71】
  • 哈希简单介绍
  • 连续数组问题
  • CSS3 多媒体查询
  • 网关过滤器(Gateway Filter)
  • 【webpack4系列】设计可维护的webpack4.x+vue构建配置(终极篇)
  • 41. 如何在MyBatis-Plus中实现批量操作?批量插入和更新的最佳实践是什么?
  • 解决DockerDesktop启动redis后采用PowerShell终端操作
  • C++初阶-list用法总结
  • 免费在线压缩pdf 压缩pdf在线免费 推荐简单好用
  • 【CTF】Nginx日志注入
  • 【算法题】63. 不同路径 II-力扣(LeetCode)-”如果起点有障碍物,那么便到不了终点“
  • WebGL颜色与纹理
  • 【制作100个unity游戏之32】unity开发属于自己的一个2d/3d桌面宠物,可以实时计算已经获取的工资
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • C++入门教程(10):for 语句
  • docker-consul
  • Java新版本的开发已正式进入轨道,版本号18.3
  • MD5加密原理解析及OC版原理实现
  • mongo索引构建
  • vue-router 实现分析
  • 初识 webpack
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 分布式任务队列Celery
  • 缓存与缓冲
  • 基于HAProxy的高性能缓存服务器nuster
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 目录与文件属性:编写ls
  • 爬虫模拟登陆 SegmentFault
  • 前端_面试
  • 跳前端坑前,先看看这个!!
  • 微信支付JSAPI,实测!终极方案
  • 用Python写一份独特的元宵节祝福
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • puppet连载22:define用法
  • RDS-Mysql 物理备份恢复到本地数据库上
  • #if和#ifdef区别
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (3)llvm ir转换过程
  • (42)STM32——LCD显示屏实验笔记
  • (C#)一个最简单的链表类
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (补充)IDEA项目结构
  • (二)fiber的基本认识
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (力扣)循环队列的实现与详解(C语言)
  • (六)DockerCompose安装与配置
  • (四)opengl函数加载和错误处理
  • (五)c52学习之旅-静态数码管