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

零成本实现3D模型跨平台迁移:Blender到Unreal Engine的无缝解决方案

零成本实现3D模型跨平台迁移:Blender到Unreal Engine的无缝解决方案 【免费下载链接】bl_datasmith Blender addon to export UE4 Datasmith format 项目地址: https://gitcode.com/gh_mirrors/bl/bl_datasmith 你是否曾遇到这样的困境:在Blender…...

3大创新突破:FlashPatch如何让Flash内容重获新生

3大创新突破:FlashPatch如何让Flash内容重获新生 【免费下载链接】FlashPatch FlashPatch! Play Adobe Flash Player games in the browser after January 12th, 2021. 项目地址: https://gitcode.com/gh_mirrors/fl/FlashPatch 如何解决2021年后Flash内容无…...

Onekey:3分钟搞定Steam游戏清单下载的终极神器

Onekey:3分钟搞定Steam游戏清单下载的终极神器 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 还在为复杂的Steam游戏清单获取流程而烦恼吗?Onekey作为一款专业的Steam D…...

如何在macOS上实现高效Android USB网络共享:HoRNDIS完整指南

如何在macOS上实现高效Android USB网络共享:HoRNDIS完整指南 【免费下载链接】HoRNDIS Android USB tethering driver for Mac OS X 项目地址: https://gitcode.com/gh_mirrors/ho/HoRNDIS Android USB网络共享是许多开发者和技术爱好者经常需要的功能&#…...

PhysX 5.1入门实战:从Hello World到刚体模拟的完整流程解析

PhysX 5.1入门实战:从Hello World到刚体模拟的完整流程解析 在游戏开发和物理仿真领域,PhysX引擎一直以其强大的性能和易用性著称。作为NVIDIA旗下的物理引擎解决方案,PhysX 5.1版本带来了更多优化和新特性。本文将带您从零开始,通…...

LiuJuan20260223Zimage v1.0作品集:当传统工笔画遇见AI生成

LiuJuan20260223Zimage v1.0作品集:当传统工笔画遇见AI生成 1. 引言:一次跨越时空的艺术对话 想象一下,你拍了一张现代都市的夜景,或者设计了一张充满未来感的数字海报,然后,你把它交给一位深谙宋元笔法的…...

hgproxy偶发性无法连接

文章目录环境症状问题原因解决方案环境 系统平台:银河麒麟 (鲲鹏) 版本:4.5.8 症状 hgproxy 4.0.33.3 出现偶发性无法连接现象,经过几分钟或几十秒或更长时间会自动恢复正常;psql 连接数据库端口正常&am…...

长上下文不可强求:从 Gemini 到 Opus,1M context 为什么还没体现出应有价值

长上下文不可强求:从 Gemini 到 Opus,1M context 为什么还没体现出应有价值 摘要 过去一年,long context 一直是大模型产品最容易被拿来宣传的能力之一。32K 不够,就上 128K;128K 还不够,就上 1M。看起来&a…...

轻松破解游戏资源加密难题:RPG Maker Decrypter使用指南

轻松破解游戏资源加密难题:RPG Maker Decrypter使用指南 【免费下载链接】RPGMakerDecrypter Tool for extracting RPG Maker XP, VX and VX Ace encrypted archives. 项目地址: https://gitcode.com/gh_mirrors/rp/RPGMakerDecrypter 直面游戏资源解密痛点 …...

Qwen1.5-1.8B GPTQ生成技术博客大纲与初稿:以“操作系统内存管理”为例

Qwen1.5-1.8B GPTQ生成技术博客大纲与初稿:以“操作系统内存管理”为例 1. 引言:当AI成为技术写作的“副驾驶” 最近在折腾一些技术分享,想写一篇关于操作系统内存管理的文章。这话题吧,说深了容易劝退,说浅了又没意…...