当前位置: 首页 > 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需要自定义序列化器,主要有以下几个原因&…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...