5. scala高阶之traits

背景

上一节介绍了scala的隐式转换,本文主要介绍scala的另外一个强大功能traits。

1. Traits

在Scala中,traits是一种非常强大的特性,它允许你定义抽象类型成员、具体方法实现、以及可以在多个类之间共享的行为。

可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似,在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可,类可以使用extends关键字继承trait。

注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends,类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字。Scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可

1.1. 定义Traits

Traits通过trait关键字定义。它们可以包含抽象成员(没有实现的方法)和具体成员(有实现的方法)。

trait Greeter {def greet(): Unitdef farewell(): Unit = println("Goodbye!") // 具体成员
}

1.2. 实现Traits

一个类可以通过extends关键字来实现一个或多个traits。如果一个trait包含抽象成员,那么实现该trait的类必须提供这些成员的具体实现。

class EnglishGreeter extends Greeter {def greet(): Unit = println("Hello!")
}

1.3. Traits的叠加(Stacking Traits)

一个类可以实现多个traits,从而组合多个行为。

trait Logger {def log(message: String): Unit = println(s"Log: $message")
}class EnglishGreeterWithLogging extends EnglishGreeter with Logger {override def greet(): Unit = {log("Greeting someone")super.greet()}
}

1.4. Traits中的抽象类型成员

Traits可以包含抽象类型成员,这些成员在实现该trait的类中必须被具体化。

trait Container {type Tdef content: T
}class IntContainer extends Container {type T = Intdef content: T = 42
}

1.5. 多重继承

在Scala中,一个类可以继承多个trait,这种多重继承的机制带来了强大的灵活性。当一个类继承了多个trait,而这些trait中包含有相同名称的方法时,可以通过不同的方式来组织方法的调用顺序和行为。

以下是一些常见的实现方式:

1.5.1. 线性化(Linearization)

当一个类实现多个traits时,tScala 的 trait 继承机制采用线性化(linearization)规则来决定方法的调用顺序。这个顺序是由 Scala 编译器根据 trait 的定义顺序和继承关系自动计算的。简单来说,最后定义的 trait 的方法会覆盖前面定义的同名方法。

trait A {def sayHello(): Unit = println("Hello from A")
}trait B {def sayHello(): Unit = println("Hello from B")
}class C extends A with B {// 默认情况下,C 的 sayHello 方法会调用 B 的 sayHello,因为 B 在 A 之后被混合进 C
}val c = new C()
c.sayHello()  // 输出: Hello from B

1.5.2. 显式调用超trait的方法

如果需要在某个 trait 中调用另一个 trait 的同名方法,可以使用 super 关键字,不过这种用法有些限制,因为它直接依赖于线性化顺序。Scala 提供了更灵活的方式,通过 self-type 注解或者辅助方法来实现。

1.5.2.1 使用 self-type 注解

有时,你可能需要在trait中引用实现该trait的类的类型。这可以通过自我类型注解来实现。

trait SelfTypeExample {this: SomeOtherTrait => // 这里指定了Self Typedef doSomething(): Unit
}trait SomeOtherTrait {def anotherMethod(): Unit
}class ExampleClass extends SomeOtherTrait with SelfTypeExample {def anotherMethod(): Unit = println("Another method called")def doSomething(): Unit = println("Doing something")
}

使用方式如下:

trait A {def sayHello(): Unit = println("Hello from A")
}trait B {this: A =>  // 指定 B 必须和 A 一起被混合override def sayHello(): Unit = {super.sayHello()  // 调用 A 的 sayHelloprintln("Hello from B")}
}class C extends A with Bval c = new C()
c.sayHello()  // 输出: Hello from A//       Hello from B
1.5.2.2 使用辅助方法

如果 self-type 注解不适合,可以使用辅助方法来显式调用其他 trait 的方法。

trait A {def sayHelloA(): Unit = println("Hello from A")def sayHello(): Unit = sayHelloA()
}trait B {def sayHelloA(): Unit  // 声明辅助方法override def sayHello(): Unit = {sayHelloA()  // 调用 A 的 sayHelloAprintln("Hello from B")}
}class C extends A with B {override def sayHelloA(): Unit = super[A].sayHelloA()  // 显式调用 A 的 sayHelloA
}val c = new C()
c.sayHello()  // 输出: Hello from A//       Hello from B

1.5.3. 使用默认参数和方法重载

有时可以通过参数化方法或重载方法来实现更复杂的行为,不过这更多依赖于具体的应用场景。

trait A {def sayHello(prefix: String = ""): Unit = println(s"$prefixHello from A")
}trait B {override def sayHello(prefix: String = ""): Unit = {super.sayHello("B's ")println(s"$prefixHello from B")}
}class C extends A with Bval c = new C()
c.sayHello()  // 输出: B's Hello from A//       Hello from B

通过以上方式,Scala 提供了丰富的机制来处理和调用多个 trait 中的同名方法,允许开发者根据需要灵活地组织方法调用顺序和行为。

1.6. Traits的早期定义(Early Definitions)

在Scala中,你可以使用早期定义来在构造器的初始化列表中引用traits中的方法。

trait EarlyDefTrait {def earlyMethod(): Unit
}class EarlyDefClass extends {val x = earlyMethod() // 在这里调用earlyMethod
} with EarlyDefTrait {def earlyMethod(): Unit = println("Early method called")
}

总结

Scala的traits提供了一种灵活且强大的代码复用机制,使得你可以定义可组合的行为和抽象类型成员。通过traits,你可以创建出高度模块化和可维护的代码库。

2. 下划线在scala中的作用

  1. 在初始化变量的时候给定默认值
  2. 将函数赋值给变量的时候,参数占位,表示赋值的是函数本身
    ,而不是函数执行结果
  3. 在变长参数函数调用过程中,传入集合元素的时候告诉编译器传递的是集合中的元素
  4. 高阶函数调用过程中,参数占位
  5. 引入包的时候,表示包下的所有类和子包
  6. 模式匹配中对于不需要的数据可以使用_代替

以上,如有错误,请不吝指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/13032.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

控件【QT】

文章目录 控件QWidgetenabledgeometrysetGeometry qrcwindowOpacityQPixmapfonttoolTipfocusPolicystyleSheetQPushButtonRadio ButtionCheck Box显示类控件QProgressBarcalendarWidget 控件 Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮,下拉框…

苹果再度砍掉AR眼镜项目?AR真的是伪风口吗?

曾经,AR游戏一度异常火热,宝可梦go让多少人不惜翻墙都要去玩,但是也没过去几年,苹果被曝出再度砍掉了AR眼镜项目,面对着市场的变化,让人不禁想问AR真的是伪风口吗? 一、苹果再度砍掉AR眼镜项目&…

《redis4.0 通信模块源码分析(一)》

【redis导读】redis作为一款高性能的内存数据库,面试服务端开发,redis是绕不开的话题,如果想提升自己的网络编程的水平和技巧,redis这款优秀的开源软件是很值得大家去分析和研究的。 笔者从大学毕业一直有分析redis源码的想法&…

日期选择控件,时间跨度最大一年。

<el-date-picker v-model"times" type"daterange" unlink-panels :picker-options"pickerOptions" :range-separator"$lang(至)":start-placeholder"$lang(开始)" :end-placeholder"$lang(结束)" :default-tim…

JDK9新特性

文章目录 新特性&#xff1a;1.模块化系统使用模块化module-info.java&#xff1a;exports&#xff1a;opens&#xff1a;requires&#xff1a;provides&#xff1a;uses&#xff1a; 2.JShell启动Jshell执行计算定义变量定义方法定义类帮助命令查看定义的变量&#xff1a;/var…

Vue Router 客户端路由解决方案:axios 响应拦截(跳转到登录页面)

文章目录 引言客户端路由 vs. 服务端路由简单的路由案例术语I Vue Router 提供的组件RouterLinkRouterViewII 创建路由器实例调用 createRouter() 函数创建路由选项III 注册路由器插件通过调用 use() 来完成注册路由器插件的职责对于组合式 API,Vue Router 给我们提供了一些组…

在游戏本(6G显存)上本地部署Deepseek,运行一个14B大语言模型,并使用API访问

在游戏本6G显存上本地部署Deepseek&#xff0c;运行一个14B大语言模型&#xff0c;并使用API访问 环境说明环境准备下载lmstudio运行lmstudio 下载模型从huggingface.co下载模型 配置模型加载模型测试模型API启动API服务代码测试 deepseek在大语言模型上的进步确实不错&#xf…

【Uniapp-Vue3】创建DB schema数据表结构

右键uniCloud文件下的database文件&#xff0c;点击“新建DB schema”&#xff0c;选择模板&#xff0c;修改文件名&#xff0c;点击“创建” 创建完成后会出现对应的文件&#xff0c;进入该文件进行配置 对文件中的必填选项&#xff0c;用户权限&#xff0c;字段进行配置 其…

1-ET框架开发环境与demo运行

所需开发环境 安装Unity模块时&#xff0c;记得安装windows Build Support&#xff08;IL2CPP&#xff09;&#xff0c;否则打包会出问题。 安装visual studio&#xff0c;因为需要安装开发组件&#xff0c;需要选择 下载MongoDB7.0.2并安装 确认MongoDB安装成功 查看计算机…

CTP查询资金费率和手续费没响应

CTP的OnRspQryInstrumentOrderCommRate()和OnRspQryInstrumentCommissionRate()和手续费率和手续费有关系&#xff0c;但是今天我通过重写这两个方法&#xff0c;并且调用ReqQryInstrumentCommissionRate()后没响应&#xff0c;查了半天发现&#xff0c;我应该把响应函数实现写…

Python爬虫实战:一键采集电商数据,掌握市场动态!

电商数据分析是个香饽饽&#xff0c;可市面上的数据采集工具要不贵得吓人&#xff0c;要不就是各种广告弹窗。干脆自己动手写个爬虫&#xff0c;想抓啥抓啥&#xff0c;还能学点技术。今天咱聊聊怎么用Python写个简单的电商数据爬虫。 打好基础&#xff1a;搞定请求头 别看爬虫…

Page Assist实现deepseek离线部署的在线搜索功能

前面文章Mac 基于Ollama 本地部署DeepSeek离线模型 实现了deepseek的离线部署&#xff0c;但是部署完成虽然可以进行问答和交互&#xff0c;也有thinking过程&#xff0c;但是没办法像官方一样进行联网搜索。今天我们介绍一款浏览器插件Page Assist来实现联网搜索&#xff0c;完…

Qt跨屏窗口的一个Bug及解决方案

如果我们希望一个窗口覆盖用户的整个桌面&#xff0c;此时就要考虑用户有多个屏幕的场景&#xff08;此窗口要横跨多个屏幕&#xff09;&#xff0c;由于每个屏幕的分辨率和缩放比例可能是不同的&#xff0c;Qt底层在为此窗口设置缩放比例&#xff08;DevicePixelRatio&#xf…

AI绘画:解锁商业设计新宇宙(6/10)

1.AI 绘画&#xff1a;商业领域的潜力新星 近年来&#xff0c;AI 绘画技术以惊人的速度发展&#xff0c;从最初简单的图像生成&#xff0c;逐渐演变为能够创造出高度逼真、富有创意的艺术作品。随着深度学习算法的不断优化&#xff0c;AI 绘画工具如 Midjourney、Stable Diffu…

逻辑回归原理

逻辑回归是一个分类算法&#xff0c;它可以处理二元分类以及多元分类。虽然它名字里面有“回归”两个字&#xff0c;却不是一个回归算法。 逻辑回归尤其是二元逻辑回归是非常常见的模型&#xff0c;训练速度很快&#xff0c;虽然使用起来没有支持向量机&#xff08;SVM&#xf…

四.4 Redis 五大数据类型/结构的详细说明/详细使用( zset 有序集合数据类型详解和使用)

四.4 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; zset 有序集合数据类型详解和使用&#xff09; 文章目录 四.4 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; zset 有序集合数据类型详解和使用&#xff09;1. 有序集合 Zset(sorted set)2. zset 有序…

AlwaysOn 可用性组副本所在服务器以及该副本上数据库的各项状态信息

目录标题 语句代码解释:1. `sys.dm_hadr_database_replica_states` 视图字段详细解释及官网链接官网链接字段解释2. `sys.availability_replicas` 视图字段详细解释及官网链接官网链接字段解释查看视图的创建语句方法一:使用 SQL Server Management Studio (SSMS)方法二:使用…

编程之路:在细节中磨砺技艺

编程之路&#xff1a;在细节中磨砺技艺 在编程的世界里&#xff0c;每一个字符、每一行代码都像是精心雕琢的艺术品&#xff0c;承载着程序员的智慧与心血。而在这个充满挑战与惊喜的旅程中&#xff0c;问题与解决方案总是如影随形。就像在开发居家养老管理系统时&#xff0c;我…

基于ArcGIS的SWAT模型+CENTURY模型模拟流域生态系统水-碳-氮耦合过程研究

流域是一个相对独立的自然地理单元&#xff0c;它是以水系为纽带&#xff0c;将系统内各自然地理要素连结成一个不可分割的整体。碳和氮是陆地生态系统中最重要的两种化学元素&#xff0c;而在流域系统内&#xff0c;水-碳-氮是相互联动、不可分割的耦合体。随着流域内人类活动…

【Linux网络编程】:URL(encode),HTTP协议,telnet工具

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://mp.csdn…