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

Scala 中trait的线性化规则(Linearization Rule)和 super 的调用行为

在 Scala 中,特质(Trait)是一种强大的工具,用于实现代码的复用和组合。当一个类混入(with)多个特质时,可能会出现方法冲突的情况。为了解决这种冲突,Scala 引入了最右优先原则(Rightmost First Rule),也称为线性化规则(Linearization Rule)

最右优先原则

最右优先原则的核心思想是:在混入多个特质时,最右边的特质会优先生效。也就是说,如果一个方法在多个特质中都有定义,那么最右边的特质中的方法会覆盖左边特质中的方法。

示例1
trait A {def greet(): String = "Hello from A"
}trait B {def greet(): String = "Hello from B"
}class C extends A with B {override def greet(): String = super.greet()
}val obj = new C
println(obj.greet())  // 输出: Hello from B

在上面的例子中:

  • 类 C 混入了特质 A 和 B

  • 根据最右优先原则,B 中的 greet 方法会覆盖 A 中的 greet 方法。

  • 因此,调用 obj.greet() 时,输出的是 B 中的实现。

线性化规则

最右优先原则是 Scala 线性化规则的一部分。Scala 会为每个类生成一个线性化顺序(Linearization Order),这个顺序决定了方法调用的优先级。

线性化顺序的生成规则
  1. 类的线性化顺序从最具体的类开始,逐步向更通用的类扩展。

  2. 混入的特质按照从右到左的顺序排列。

  3. 每个特质只会在线性化顺序中出现一次。

示例2
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = "Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet())  // 输出: Hello from C

在这个例子中:

  • 类 D 的线性化顺序是:D -> C -> B -> A

  • 根据最右优先原则,C 中的 greet 方法会覆盖 B 中的 greet 方法。

  • 因此,调用 obj.greet() 时,输出的是 C 中的实现。

super 的调用

在特质中,super 的调用是动态绑定的,它会根据线性化顺序调用下一个特质或类中的方法。

示例3
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = s"${super.greet()} and Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet())  // 输出: Hello from A and Hello from B and Hello from C

如果你还是有疑问,接下来,是更加具体的分析:
 

在示例3中,输出的是Hello from A and Hello from B and Hello from C,而不是 Hello from A and Hello from C and Hello from B。这看起来似乎与最右优先原则相矛盾,但实际上这是由 Scala 的线性化规则(Linearization Rule)决定的。

线性化规则详解

Scala 的线性化规则决定了方法调用的顺序。具体来说,当一个类混入多个特质时,Scala 会生成一个线性化顺序,这个顺序决定了 super 调用的行为。

线性化顺序的生成规则
  1. 从最具体的类开始,逐步向更通用的类扩展。

  2. 混入的特质按照从右到左的顺序排列

  3. 每个特质只会在线性化顺序中出现一次

在示例3中:

class D extends B with C
  • D 的线性化顺序是:D -> C -> B -> A

线性化顺序的解释
  1. D:最具体的类。

  2. C:因为 C 是最右边的特质,所以它排在 B 前面。

  3. BB 是左边的特质,排在 C 后面。

  4. AA 是 B 和 C 的共同父特质,排在最后。

因此,D 的线性化顺序是:D -> C -> B -> A

super 的调用行为

在 Scala 中,super 的调用是动态绑定的,它会根据线性化顺序调用下一个特质或类中的方法。

例子分析
trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = s"${super.greet()} and Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet())  // 输出: Hello from A and Hello from B and Hello from C
  1. D 中的 greet 方法

    • 调用 super.greet(),根据线性化顺序,super 指向 C

  2. C 中的 greet 方法

    • 调用 super.greet(),根据线性化顺序,super 指向 B

  3. B 中的 greet 方法

    • 调用 super.greet(),根据线性化顺序,super 指向 A

  4. A 中的 greet 方法

    • 返回 "Hello from A"

  5. 方法调用的堆栈

    • A 返回 "Hello from A"

    • B 在其基础上追加 " and Hello from B",得到 "Hello from A and Hello from B"

    • C 在其基础上追加 " and Hello from C",得到 "Hello from A and Hello from B and Hello from C"

为什么不是 Hello from A and Hello from C and Hello from B

  • 因为 super 的调用是根据线性化顺序动态绑定的,而不是简单地按照最右优先原则直接覆盖。

  • 线性化顺序是 D -> C -> B -> A,所以 C 的 super 指向 BB 的 super 指向 A

  • 因此,C 的 greet 方法会先调用 B 的 greet 方法,而 B 的 greet 方法会调用 A 的 greet 方法。

总结

  • 最右优先原则:决定了特质的优先级,最右边的特质会优先生效。

  • 线性化规则:决定了 super 的调用顺序,super 会根据线性化顺序动态绑定到下一个特质或类。

  • 在示例3中,线性化顺序是 D -> C -> B -> A,因此输出的顺序是 Hello from A and Hello from B and Hello from C

在示例2中,为什么输出是 Hello from C,而不是 Hello from A and Hello from C?


代码分析

trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = "Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet())  // 输出: Hello from C
  1. 特质的继承关系

    • B 和 C 都继承自 A,并且都重写了 greet 方法。

    • D 混入了 B 和 C,并且重写了 greet 方法,调用了 super.greet()

  2. 线性化顺序

    • 当 D 混入 B 和 C 时,Scala 会生成一个线性化顺序。线性化顺序的规则是:

      • 从最具体的类开始,逐步向更通用的类扩展。

      • 混入的特质按照从右到左的顺序排列。

      • 每个特质只会在线性化顺序中出现一次。

    • 对于 class D extends B with C,线性化顺序是:D -> C -> B -> A

  3. super 的调用行为

    • 在 D 的 greet 方法中,super.greet() 会根据线性化顺序调用下一个特质或类中的 greet 方法。

    • 线性化顺序是 D -> C -> B -> A,因此 super.greet() 会调用 C 中的 greet 方法。

  4. C 中的 greet 方法

    • C 中的 greet 方法直接返回 "Hello from C"没有调用 super.greet()

    • 因此,C 的 greet 方法不会继续调用 B 或 A 的 greet 方法。


为什么输出是 Hello from C

  • 在 D 的 greet 方法中,super.greet() 调用的是 C 的 greet 方法。

  • C 的 greet 方法直接返回 "Hello from C",没有继续调用 super.greet()(即没有调用 B 或 A 的 greet 方法)。

  • 因此,最终的输出是 "Hello from C"


为什么不是 Hello from A and Hello from C

  • 如果希望输出 Hello from A and Hello from C需要在 C 的 greet 方法中显式调用 super.greet(),将 A 的行为与 C 的行为组合起来。

  • 例如:

trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}

修改后,C 的 greet 方法会先调用 A 的 greet 方法,然后追加 " and Hello from C"。此时,输出会是 Hello from A and Hello from C


修改后的代码

trait A {def greet(): String = "Hello from A"
}trait B extends A {override def greet(): String = "Hello from B"
}trait C extends A {override def greet(): String = s"${super.greet()} and Hello from C"
}class D extends B with C {override def greet(): String = super.greet()
}val obj = new D
println(obj.greet())  // 输出: Hello from A and Hello from C

总结

  • 默认行为:在 C 的 greet 方法中,如果没有调用 super.greet(),则只会执行 C 的逻辑,输出 Hello from C

  • 组合行为:如果希望将父特质的行为与当前特质的行为组合起来,需要在重写方法时显式调用 super.greet()

  • 线性化顺序super 的调用是根据线性化顺序动态绑定的,线性化顺序决定了方法调用的优先级。

相关文章:

Scala 中trait的线性化规则(Linearization Rule)和 super 的调用行为

在 Scala 中,特质(Trait)是一种强大的工具,用于实现代码的复用和组合。当一个类混入(with)多个特质时,可能会出现方法冲突的情况。为了解决这种冲突,Scala 引入了最右优先原则&#…...

C++入门——引用

C入门——引用 一、引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。这就好比《水浒传》中,一百零八位好汉都有自己的绰号。通过&…...

深度学习模型组件之优化器—Lookahead:通过“快慢”两组优化器协同工作,提升训练稳定性

深度学习模型组件之优化器—Lookahead:通过“快/慢”两组优化器协同工作,提升训练稳定性 文章目录 深度学习模型组件之优化器—Lookahead:通过“快/慢”两组优化器协同工作,提升训练稳定性1. Lookahead优化器的背景2. Lookahead优…...

K8s 1.27.1 实战系列(五)Namespace

Kubernetes 1.27.1 中的 ​Namespace​(命名空间)是集群中实现多租户资源隔离的核心机制。以下从功能、操作、配置及实践角度进行详细解析: 一、核心功能与特性 ​1、资源隔离 Namespace 将集群资源划分为逻辑组,实现 Pod、Service、Deployment 等资源的虚拟隔离。例如,…...

Spring Boot整合ArangoDB教程

精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、环境准备 JDK 17Maven 3.8Spring Boot 3.2ArangoDB 3.11(本地安装或Docker运行) Docker启动ArangoDB docker run -d --name ar…...

虚幻基础:动画层接口

文章目录 动画层:动画图表中的函数接口:名字,没有实现。动画层接口:由动画蓝图实现1.动画层可直接调用实现功能2.动画层接口必须安装3.动画层默认使用本身实现4.动画层也可使用其他动画蓝图实现,但必须在角色蓝图中关联…...

从 GitHub 批量下载项目各版本的方法

一、脚本功能概述 这个 Python 脚本的主要功能是从 GitHub 上下载指定项目的各个发布版本的压缩包(.zip 和 .tar.gz 格式)。用户需要提供两个参数:一个是包含项目信息的 CSV 文件,另一个是用于保存下载版本信息的 CSV 文件。脚本…...

一、对lora_sx1278v1.2模块通信记录梳理

一、通信测试: 注意: 1、检查供电是否满足。 2、检测引脚是否松动或虚焊。 3、检测触发是否能触发。 引脚作用: SPI:通信(仅作一次初始化,初始化后会进行模块通信返回测试,返回值和预定值相否即…...

Java在word中动态增加表格行并写入数据

SpringBoot项目中在word中动态增加表格行并写入数据,不废话,直接上配置和代码: 模板内容如下图所示: 模板是一个空word表格即可,模板放在resources下的自定义目录下,如下图示例。 实体类定义如下: @Data @AllArgsConstructor @NoArgsConstructor public class Person …...

[通讯协议]232通信

RS-232 简介 RS-232是一种广泛应用的串行通信接口标准,使用的协议就是串口协议。 通信能力 单端信号传输:信号以地线为参考,逻辑“1”为-3V至-15V,逻辑“0”为3V至15V。点对点通信:仅支持两个设备之间的通信&#x…...

Refreshtoken 前端 安全 前端安全方面

网络安全 前端不需要过硬的网络安全方面的知识,但是能够了解大多数的网络安全,并且可以进行简单的防御前两三个是需要的 介绍一下常见的安全问题,解决方式,和小的Demo,希望大家喜欢 网络安全汇总 XSSCSRF点击劫持SQL注入OS注入请求劫持DDOS 在我看来,前端可以了解并且防御前…...

EasyRTC嵌入式音视频通话SDK:基于ICE与STUN/TURN的实时音视频通信解决方案

在当今数字化时代,实时音视频通信技术已成为人们生活和工作中不可或缺的一部分。无论是家庭中的远程看护、办公场景中的远程协作,还是工业领域的远程巡检和智能设备的互联互通,高效、稳定的通信技术都是实现这些功能的核心。 EasyRTC嵌入式音…...

AI终章.展望未来2026-2030年预测与DeepSeek的角色

人工智能(AI)近年来发展迅速,正在改变行业、商业模式以及我们与技术互动的方式。展望2026-2030年,预计在多模态AI、自主代理和自动化驱动的新职业创造方面将出现革命性发展。本章将探讨这些趋势,以及DeepSeek将如何在这…...

PyTorch系列教程:编写高效模型训练流程

当使用PyTorch开发机器学习模型时,建立一个有效的训练循环是至关重要的。这个过程包括组织和执行对数据、参数和计算资源的操作序列。让我们深入了解关键组件,并演示如何构建一个精细的训练循环流程,有效地处理数据处理,向前和向后…...

【面试】Zookeeper

Zookeeper 1、ZooKeeper 介绍2、znode 节点里面的存储3、znode 节点上监听机制4、ZooKeeper 集群部署5、ZooKeeper 选举机制6、何为集群脑裂7、如何保证数据一致性8、讲一下 zk 分布式锁实现原理吧9、Eureka 与 Zk 有什么区别 1、ZooKeeper 介绍 ZooKeeper 的核心特性 高可用…...

电力系统中各参数的详细解释【智能电表】

一、核心电力参数 电压 (Voltage) 单位:伏特(V) 含义:电势差,推动电流流动的动力 类型:线电压(三相系统)、相电压,如220V(家用)或380V&#xff…...

前端系统测试(单元、集成、数据|性能|回归)

有关前端测试的面试题 系统测试 首先,功能测试部分。根据资料,单元测试是验证最小可测试单元的正确性,比如函数或组件。都提到了单元测试的重要性,强调其在开发早期发现问题,并通过自动化提高效率。需要整合我搜索到的资料中的观点,比如单元测试的方法(接口测试、路径覆…...

软件开发过程总揽

开发模型 传统开发模型 瀑布模型 #mermaid-svg-yDNBSwh3gDYETWou {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-yDNBSwh3gDYETWou .error-icon{fill:#552222;}#mermaid-svg-yDNBSwh3gDYETWou .error-text{fill:#…...

VBA第二十期 VBA最简单复制整张表格Cells的用法

前面讲过复制整张表格的方法,使用语句Workbooks("实例.xlsm").Sheets("表格1").Copy Workbooks(wjm).Sheets(1)实现,这里用我们熟悉的Cells属性也可以实现整表复制。实例如下: Sheets("全部").Activate Cells…...

Redis为什么要自定义序列化?如何实现自定义序列化器?

在 Redis中,通常会使用自定义序列化器,那么,Redis为什么需要自定义序列化器,该如何实现它? 1、为什么需要自定义序列化器? 整体来说,Redis需要自定义序列化器,主要有以下几个原因&…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

MMaDA: Multimodal Large Diffusion Language Models

CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...