scala类型系统:15) 协变与逆变

2024-08-27 12:48

本文主要是介绍scala类型系统:15) 协变与逆变,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文链接:http://hongjiang.info/scala-covariance-and-contravariance/#comment-4003

对于一个带类型参数的类型,比如 List[T],如果对A及其子类型B,满足 List[B]也符合 List[A]的子类型,那么就称为covariance(协变),如果 List[A]是 List[B]的子类型,即与原来的父子关系正相反,则称为contravariance(逆变)

协变:

 _____               _____________ 
|     |             |             |
|  A  |             |  List[ A ]  |
|_____|             |_____________|^                       ^ |                       | _____               _____________ 
|     |             |             |
|  B  |             |  List[ B ]  |
|_____|             |_____________|  

逆变:

 _____               _____________ 
|     |             |             |
|  A  |             |  List[ B ]  |
|_____|             |_____________|^                       ^ |                       | _____               _____________ 
|     |             |             |
|  B  |             |  List[ A ]  |
|_____|             |_____________|  

如果一个类型支持协变或逆变,则称这个类型为variance(翻译为可变的或变型),否则称为invariant(不可变的)

在Java里,泛型类型都是invariant,比如 List<String> 并不是 List<Object> 的子类型。Java并不支持声明点变型(declaration-site variance,即在定义一个类型时声明它为可变型,也称definition-site),而scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),如:

trait List[+T] // 在类型定义时(declaration-site)声明为协变 

这样会把List[String]作为List[Any]的子类型。

不过Java支持使用点变型(use-site variance),所谓“使用点“,也就是在声明变量时:

List<? extends Object> list = new ArrayList<String>();

scala为了兼容java泛型通配符的形式,引入存在类型(existential type,后边再讲)时,也支持了使用点变型(use-site variance)

scala> val a : List[_ <: Any] = List[String]("A")
a: List[_] = List(A)    

要注意variance并不会被继承,父类声明为variance,子类如果想要保持,仍需要声明:

scala> trait A[+T]scala> class C[T] extends A[T]  // C是invariant的scala> class X; class Y extends X;scala> val t:C[X] = new C[Y]
<console>:11: error: type mismatch; found   : C[Y]required: C[X]
Note: Y <: X, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)

必须也对C声明为协变的才行:

scala> class C[+T] extends A[T]scala> val t:C[X] = new C[Y]
t: C[X] = C@6a079142

对于协变与逆变的使用,参考《Effective Java》的PECS原则。这里有篇旧文也可供参考。


这篇关于scala类型系统:15) 协变与逆变的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1111719

相关文章

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

C# WebAPI的几种返回类型方式

《C#WebAPI的几种返回类型方式》本文主要介绍了C#WebAPI的几种返回类型方式,包括直接返回指定类型、返回IActionResult实例和返回ActionResult,文中通过示例代码介绍的... 目录创建 Controller 和 Model 类在 Action 中返回 指定类型在 Action

C++简单日志系统实现代码示例

《C++简单日志系统实现代码示例》日志系统是成熟软件中的一个重要组成部分,其记录软件的使用和运行行为,方便事后进行故障分析、数据统计等,:本文主要介绍C++简单日志系统实现的相关资料,文中通过代码... 目录前言Util.hppLevel.hppLogMsg.hppFormat.hppSink.hppBuf

python中的鸭子类型详解

《python中的鸭子类型详解》鸭子类型是Python动态类型系统的灵魂,它通过强调“行为”而非“类型”,赋予了代码极大的灵活性和表现力,本文给大家详细介绍python中的鸭子类型,感兴趣的朋友一起看... 目录1. 核心思想:什么是鸭子类型?2. 与“传统”静态类型语言的对比3. python 中无处不在

Java枚举类型深度详解

《Java枚举类型深度详解》Java的枚举类型(enum)是一种强大的工具,它不仅可以让你的代码更简洁、可读,而且通过类型安全、常量集合、方法重写和接口实现等特性,使得枚举在很多场景下都非常有用,本文... 目录前言1. enum关键字的使用:定义枚举类型什么是枚举类型?如何定义枚举类型?使用枚举类型:2.

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

Oracle数据库在windows系统上重启步骤

《Oracle数据库在windows系统上重启步骤》有时候在服务中重启了oracle之后,数据库并不能正常访问,下面:本文主要介绍Oracle数据库在windows系统上重启的相关资料,文中通过代... oracle数据库在Windows上重启的方法我这里是使用oracle自带的sqlplus工具实现的方

Python中Json和其他类型相互转换的实现示例

《Python中Json和其他类型相互转换的实现示例》本文介绍了在Python中使用json模块实现json数据与dict、object之间的高效转换,包括loads(),load(),dumps()... 项目中经常会用到json格式转为object对象、dict字典格式等。在此做个记录,方便后续用到该方

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环