Scala中的协变点、逆变点、不变点如何确定?

news2025/1/16 15:53:45

阅读《scala编程》时,我们知道了类的类型参数是可以型变(variance)的。型变包含以下三种:

  • 协变(convariant):如果S是T的子类型,则C1[S]也是C1[T]的子类型,则称C1在类型参数T上是协变的。通过定义时再T前面加上+实现。class C1[+T]。P393
  • 不变(nonvariant):不同类型的队列之间永远不存在子类型关系。泛型默认是不变的。 P394
  • 逆变(contravariance):如果S是T的子类型,则C1[T]是C1[S]的子类型。通过定义时再T前面加上-实现。class C1[-T]

类或特质定义中能出现类型参数的所有位置都会被归类成不同的“点”,这些点可分为

  • 协变点(positive)
  • 逆变点(negetive)
  • 不变点(neutral)。

类型参数在使用上遵守以下规则:

  • 用+注解的类型参数只能用在协变点。
  • 用-注解的类型参数只能用在逆变点。
  • 不用注解的类型参数,可以用再任意点。(也是唯一能用在不变点的类型参数)

令人困扰的是,如何确定,各个位置是什么类型的点呢?书中如下结论到底是怎么来的?

abstract class Cat[-T, +U] {
    def meow[W](volume: T, listener: Cat[U, T]): Cat[Cat[U, T], U]
}
// 书中给的各类型参数的结论如下:+表示协变点,-表示逆变点
abstract class Cat[-T, +U] {
    def meow[W-](volume: T-, listener: Cat[U+, T-]-): Cat[Cat[U+, T-]-, U+]+
}

确定点的类型有如下基本规则和例外情况,按照基本规则和例外情况就可以分析出所有点的类型了。

基本规则:

  1. 从类(或特质)的定义处开始,所有类型参数按照类的嵌套定义生成一颗树。如下图所示。
  2. 树顶部是协变点(positive)
  3. 更深嵌套的层次的点默认跟它上一层的类型相同

在这里插入图片描述
如果有更多其他成员在meow一层定义(如def、 val、 var、 type等),则meow一层还有更多分支。

例外情况:

  1. 方法入参的类型参数的位置被归纳为“翻转”类,具有以下性质:、
    • 逆变点翻转为协变点
    • 协变点翻转为逆变点
    • 不变点翻转后仍为不变点
  2. 方法本身的类型参数的位置也属于“翻转”类。如def f[T]中T的位置。
  3. 类型的类型参数的位置(原文是 type argument position of a type),如a: C[T]中的T,根据C在定义时,T的声明的型变决定,具体的:
    • +T 则类型保持不变
    • -T 则类型变为“翻转”
    • T 则类型变为“不变点”(neutral)

解释书中的样例

abstract class Cat[-T, +U] {
    def meow[W](volume: T, listener: Cat[U, T]): Cat[Cat[U, T], U]
}
// 书中给的各类型参数的结论如下:+表示协变点,-表示逆变点
abstract class Cat[-T, +U] {
    def meow[W-](volume: T-, listener: Cat[U+, T-]-): Cat[Cat[U+, T-]-, U+]+
}

具体解释,以下A-J表示位置:

// 顶点是+的,所以根据基本规则3,则应该有底层的A B C D四个位置都是+的
abstract class Cat[-T, +U] {
    def meow[A](volume: B, listener: C): D
}
// 根据例外情况2,则A翻转,由+变成-的
abstract class Cat[-T, +U] {
    def meow[A-](volume: B, listener: C): D
}

// 根据例外情况1,B,C都翻转成-的,D没有例外,仍是+
abstract class Cat[-T, +U] {
    def meow[A-](volume: B-, listener: C-): D+
}
//C位置继续往下嵌套为Cat[U, T],所以Cat[E, F]的E,F两个位置根据基本规则3,继承C处类型-,
// 然后根据例外情况3,E处定义时为-T,需翻转为+;F处定义时为+U,保持不变为-,则C位置最终为Cat[E+, F-]-
abstract class Cat[-T, +U] {
    def meow[A-](volume: B-, listener: Cat[E+, F-]-): D+
}
// D位置也类似C位置的经过两步分析,第1步得到 Cat[G-, H+]+,第2步得到Cat[Cat[I+, J-]-, H+]+所以最终为:
abstract class Cat[-T, +U] {
     def meow[A-](volume: B-, listener: Cat[E+, F-]-): Cat[Cat[I+, J-]-, H+]+
}

把位置处带入类型参数,即为书本上的结果:
abstract class Cat[-T, +U] {
    def meow[W-](volume: T-, listener: Cat[U+, T-]-): Cat[Cat[U+, T-]-, U+]+
}

参考

  • 参考1
  • 参考2
  • 参考3

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode 208. 实现 Trie (前缀树)【字典树(前缀树)的介绍与思路整理】

题目 Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。 请你实现 Trie 类: Trie() 初始化前缀树对象。…

HCL Notes/Domino 12.0.2版本正式发布

大家好,才是真的好。 之前代号为多瑙河版本的Notes/Domino产品,昨天晚上正式露出了神秘的面纱,版本号也正式定为12.0.2。从版本上来看,是12.0版本的小版本,但从功能和特性上来说,这完全就是一个大版本。 …

Duboo优雅关闭(附源码分析)

Dubbo优雅关闭 1. 关闭有什么问题 当服务提供方要上线的时候,一般是通过部署系统完成实例重启。在这个过程中,服务提供方的团队并不会事先告诉调用方他们需要操作哪些机器,从而让调用方去事先切走流量。而对调用方来说,它也无法…

LeetCode刷题复盘笔记—一文搞懂动态规划之72. 编辑距离问题(动态规划系列第四十一篇)

今日主要总结一下动态规划的一道题目,72. 编辑距离 题目:72. 编辑距离 Leetcode题目地址 题目描述: 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&…

FME对调查云平台完成变更调查照片的批量迁移

目录 前言 二、实际步骤 1.准备基础数据 2.模拟登录 3.获取图斑标识码 4.获取图形信息 5.通过空间位置关系过滤不合格照片 5.通过深度学习模型过滤照片特征错误图斑 6.照片迁移 总结 前言 又到了一年一度国土变更调查的苦日子,因为项目规则原因,…

【架构设计】你的应用该如何分层呢?

前言 最近review公司的代码,发现现在整个代码层级十分混乱,一个service类的长度甚至达到了5000多行。而且各种分层模型DTO、VO乱用, 最终出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵一发而动全身等问题。 我们在吸取了阿里…

spring之aop底层实现

1.aop之ajc增强 什么是ajc增强? ajc是aop的另外一种实现, 通过aspectj编码器来改动class源文件实现aop 2.aop之agent增强 什么是agent增强? agent是aop的另外一种实现,是通过类加载时改动class类 3.aop之proxy增强-jdk代理 …

Mac系统入门之电脑卡死怎么办

当你兴冲冲的从菜鸡驿站提回来一台新的电脑,你欣喜若狂,迫不及待的拆开快递箱,里面是一台苹果电脑,这时,你不禁抓耳挠腮:Mac系统怎么用啊? 下面,这篇专栏教你如何入门Mac系统 https://blog.csdn.net/cyyyyds857/category_12163999.html –––––前言 你正兴致勃勃的写着…

mysql中字符串拼接、填充和切片

一、本文主要结构 在编程过程往往会遇到,多个字符串需要进行拼接或者填充固定值或者截取部分数据,本文主要实战下面四个函数 concat(str1, str2,…):字符串进行拼接 lpap():左边填充 rpad(&…

【C语言】指针经典题分析

🏖️作者:malloc不出对象 ⛺专栏:《初识C语言》 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录前言一、指针与数组经典题解析二、经…

创新的概念、设计和生产鞋类和鞋类软件丨Jevero及Botcha 3D功能简介

Jevero功能简介 重新定义鞋类发展 Jevero是图案工程师、鞋类开发人员和设计师的优秀支持。从设计到生产都在一个工具中完成。 产品功能及优势 01、更快的开发,缩短上市时间 Jevero使您的图案工程师、鞋类开发人员、工业设计师之间能够进行协作。利用Rhino平台产…

两数相加 java语言

leetcode地址:两数相加描述:给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除…

DevData Talks | 张乐、茹炳晟、应阔浩、任晶磊:研发效能实践的2022年复盘和展望

跌宕起伏的 2022 年已经成为过去时。在这一年,我们既看到外部环境变幻莫测,也看到研发效能行业沉下心来稳步发展,从宏大的概念和价值,转向具体的问题,和务实、可行动的解决方案。 在新一年的开端上回望,20…

靶机测试CyNix笔记

靶机测试CyNix笔记 靶机描述 Level: Intermediate-HardUser flag: user.txtRoot flag: root.txtDescription: It’s a Boot2Root machine. The machine is VirtualBox compatible but can be used in VMWare as well (not tested but it should work). The DHCP will assign …

webpack中模块加载器Loader、插件plugins、optimization属性

目录 模块加载器(Loader) 导入css文件 加载图片 方法一 方法二 转换es6(向下兼容es5) html代码组件导入导出 导入less文件 自定义loader(Markdown文件加载器) markdown-loader.js文件 webpack.c…

【Linux】程序的翻译过程(图示详解)

因为淋过雨,所以懂的为别人撑伞;因为迷茫过,所以懂得为别人指路。 我们都知道写好代码后,编译器会帮助我们把代码生成可执行程序,细加了解又会知道程序的生成又分为四步:预处理、编译、汇编、链接。那么这四…

STM32MP157驱动开发——Linux IIO驱动(上)

STM32MP157驱动开发——Linux IIO驱动(上 )0.前言一、IIO 子系统简介1.iio_dev 结构体2.iio_dev 申请与释放3.iio_dev 注册与注销4.iio_info5.iio_chan_spec二、驱动开发1. ICM20608 的 IIO 驱动框架搭建2.IIO 设备申请与初始化3.基于以上驱动框架开发 I…

[JavaEE初阶] 线程安全问题的原因和解决方案

努力努力,月薪过亿!!! 格局打开~~~ 文章目录前言1. 线程安全问题的概念2. 线程安全问题的原因3. 线程安全问题解决--加锁3. synchronized4. 死锁4.1 产生死锁的情况4.3 产生死锁的必要条件4.4 避免死锁的方法前言 线程安全这里可能会出道面试题,在日常工作中也是很重要的内容.…

MathType公式对齐不正确

MathType公式对齐不正确1.软件环境⚙️2.问题描述🔍3.解决方法🐡4.1.通过标尺对齐4.2.通过输入具体的制表符位置对齐1.软件环境⚙️ Windows10 教育版64位 Word 2021 MathType 7 2.问题描述🔍 在使用Word写论文的时候,总是避免不…

JavaScript 模块:理解模块系统

前言 现代JavaScript开发毋庸置疑会遇到代码量大和广泛使用第三方库的问题。解决这个问题的方案通常需要把代码拆分成很多部分,然后再通过某种方式将它们连接起来。 在ECMAScript 6模块规范出现之前,虽然浏览器原生不支持模块的行为, 但也迫…