Scala之泛型详解

news2024/11/16 21:40:04

泛型用于指定类或方法可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性。泛型的典型应用场景是集合及集合中的方法参数,可以说同 Java 一样,Scala 中泛型无处不在,具体可查看 Scala 的 API

泛型基础

泛型,即“参数化类型”,就是将类型由原来的具体类型参数化,然后在使用/调用时传入具体的类型。泛型的本质是为了参数化类型,在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、特质和方法中,分别被称为泛型类泛型方法泛型特质

泛型类:指定类可以接受任意类型参数。在创建对象时, 可以指定具体的类型,传参值要与指定类型一致,或者不指定类型,基于传参值自动推断类型。

// 定义泛型类 Student ,泛型 T 和 N
class Student[T,N](name:T,var age:N) {
      def show():Unit = {
          println(s"name:$name, age:$age")
      }
}
// 传参值要写入泛型指定的类型
val a = new Student[String,Int]("张三",33)
a.show()
// 不指定类型,自动推断类型 
val b = new Student("李四","六十")
b.show() 

在这里插入图片描述

泛型方法:指定方法可以接受任意类型参数。在调用方法时, 可以明确具体的数据类型,或者不指定类型,基于传参值自动推断类型。

// 定义泛型方法show
def show[T](x:T):T = { x }
// 指定了方法类型
val a = show[String]("hi")
// 自动推断类型
val b = show(33)
val c = show(9.9)

在这里插入图片描述

泛型特质:指的是把泛型定义到特质的声明上, 即:该特质中的成员的参数类型是由泛型来决定的. 在定义泛型特质的子类或者子单例对象时, 明确具体的数据类型。

// 泛型特质 Person
trait Person[T]{
      def show():T
}

// 泛型类 Student , 继承指定为String类型的特质
class Student[N](name:String,age:N) extends Person[String]{
      override def show():String = {
          "name:"+name+", age:"+age
      }
}
// 参数要写入泛型定义的指定的类型,不指定泛型,会基于传递的参数值自动推断类型
val a = new Student[Int]("张三",33)
a.show() 
val b = new Student("李四","六十")
b.show() 

在这里插入图片描述

泛型标记符:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(一般类型)
K - Key(键)
V - Value(值)
N - Number(数值类型)
_ - 表示不确定的类型

类型变量界定

类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩小泛型的具体范围,即限定该泛型必须从哪个类继承、或者必须是哪个类的父类。这就是泛型的上界与下界。
上界 :使用 [T <: 类型] 表示给类型添加一个上界,表示泛型参数必须是从该类型本身或该类型的子类型。
下界 :使用 [T >: 类型] 表示给类型添加一个下界,表示泛型参数必须是从该类型本身或该类型的父类型。
示例:

// T 必须继承自实现了Comparable的类, 才能执行compareTo方法,否则报错
def compare[T <: Comparable[T]](x:T, y:T) = { 
    if(x.compareTo(y) > 0) x else y 
} 
compare("A", "B")

在这里插入图片描述

视图界定

上面讲的类型变量界定建立在类继承层次结构的基础上,但有时候这种限定不能满足实际要求,如果希望跨越类继承层次结构时,可以使用视图界定来实现,其原理是通过隐式转换来实现。

隐含参数和方法也可以定义隐式转换,称作视图。视图的绑定从另一个角度看就是 implicit 的转换。主要用在两个场合:① 当一个 T 类型的变量 t 要装换成 A 类型时。 ② 当一个类型 T 的变量 t 无法拥有 A 类型的 a 方法或变量时。其实视图的绑定是为了更方便的使用隐式装换,视图界定利用 <% 符号来实现。
示例代码

// 除可以接受 String 及其子类外,也可以接受可以隐式转换为 String 类型的类型
def concat[T <% String](x:T, y:T):String = { x + y } 
// 定义一个 Int 转换 String 的隐式函数
implicit def intToString(i:Int) = i.toString
// Int类型会隐式转换为String类型,然后做字符串拼接
concat(165,173)

在这里插入图片描述

协变、逆变与不变

协变(+)

C[+T]:当类型 B 是类型 A 的子类型时,则 C[B] 也可以认为是 C[A] 的子类型,即 C[B] 可以泛化为 C[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变 (covariance) 。比如 List[+T]

class Person  //父类
class Student extends Person //子类
class Covariance[+T]  //协变
val a:Covariance[Person] = new Covariance[Student] // Covariance[Person] 是 Covariance[Student] 的父类

在这里插入图片描述

逆变(-)

C[-T]:当类型 B 是类型 A 的子类型,则 C[A] 反过来可以认为是 C[B] 的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance) 。比如Queue[-T]

class Person  //父类
class Student extends Person //子类
class Contravariance[-T]  //逆变
val b:Contravariance[Student] = new Contravariance[Person] // Contravariance[Person] 是 Contravariance[Student] 的子类

在这里插入图片描述

不变

C[T]:无论类型 B 与类型 A 是什么关系, C[A]C[B] 没有从属关系,称为不变(invariance) 。

class Person  //父类
class Student extends Person //子类
class Invariance[T]  //不变
val a:Invariance[Person] = new Invariance[Student] // Invariance[Person] 与 Invariance[Student] 没有从属关系
val b:Invariance[Student] = new Invariance[Person] // Invariance[Person] 与 Invariance[Student] 没有从属关系

在这里插入图片描述

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

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

相关文章

基于粒子群算法的无约束优化问题求解

基于粒子群算法的无约束优化问题求解 1 引言2 粒子群算法2.1 粒子群优化原理2.2 粒子群算法寻优策略与参数控制粒子群算法流程 3 粒子群算法求解无约束优化问题3.1 粒子群算法求解Sphere函数&#xff08;单峰测试函数&#xff09;3.2 Schwefels Problem 2.26&#xff08;多峰测…

chatgpt赋能python:Win7怎么安装Python?

Win7怎么安装Python&#xff1f; 如果你正在使用Windows 7操作系统&#xff0c;想要安装Python&#xff0c;那么你来对了地方。Python是一种利用广泛的编程语言&#xff0c;可用于开发Web应用程序、数据分析和科学计算、机器学习等各种领域。 在此篇文章中&#xff0c;我们会…

MATLAB matlab人脸识别源码+使用说明+操作说明内容清晰适合新手

程序运行界面&#xff1a; 部分代码&#xff1a; function varargout facerecg(varargin) % FACERECG MATLAB code for facerecg.fig % FACERECG, by itself, creates a new FACERECG or raises the existing % singleton*. % % H FACERECG returns the hand…

Revit中如何导入、导出明细表?

Revit中明细表的作用非常大&#xff0c;项目中的数据归类整理及统计都离不开它&#xff0c;今天给大家分享一下如何在Revit中进行明细表标准的导出及导入&#xff0c;减少在实际项目中的重复性工作。 1、首先在Revit中新建一个项目文件&#xff0c;在平面视图中随便画几条管道…

用git下载gitee上的项目资源

目录 用git下载gitee上的项目资源 用git 的clone 命令 然后到gitee上复制相关的下载地址&#xff1a; 粘贴到clone后面即可&#xff08;注意地址与clone之间有空格&#xff01;&#xff01;&#xff01;&#xff09; 运行结果&#xff1a; 用git下载gitee上的项目资源 用git…

学习vue2笔记

学习vue2笔记 文章目录 学习vue2笔记脚手架文件结构关于不同版本的Vuevue.config.js配置文件ref属性props配置项mixin(混入)插件scoped样式总结TodoList案例webStorage组件的自定义事件全局事件总线&#xff08;GlobalEventBus&#xff09;消息订阅与发布&#xff08;pubsub&am…

LeetCoda 打卡day53--动态规划之最长子序列

一个人的朝圣 — LeetCode打卡第52天 知识总结 Leetcode 1143. 最长公共子序列题目说明代码说明 Leetcode 53. 最大子数组和题目说明代码说明 Leetcode 1035. 不相交的线题目说明代码说明 知识总结 今天几道最长子序列的题目, 都可以用一个固定的模版完成. 理解其中递推公式的…

字典序最小回文串

字典序最小回文串 题目解读 给你一个由 小写英文字母 组成的字符串 s &#xff0c;你可以对其执行一些操作。在一步操作中&#xff0c;你可以用其他小写英文字母 替换 s 中的一个字符。 请你执行 尽可能少的操作 &#xff0c;使 s 变成一个 回文串 。如果执行 最少 操作次数…

DAY38——动态规划

步骤&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 题目一. 斐波那契数列 1. 确定dp数组以及下标的含义 dp[i]的定义为&#xff1a;第i个数的斐波那契数值是dp[i] 2. 确定递推公式 状态…

FFmpegFrameGrabber视频抽帧工具类

Bytedeco 通过视频链接进行关键帧抽取图片&#xff0c;利用FFmpegFrameGrabber对视频流进行抽帧处理。 一、引入POM依赖 <dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>1.4.1</version><…

TCP 拥塞状态机演进

下面是 TCP 拥塞状态机&#xff1a; 但它只是冰山一角&#xff0c;这只是 loss-based 状态机&#xff0c;实现一个完全的 delay-based cc 就对不上这个状态机。 该状态机来自 RFC5681&#xff0c;源自 RFC2581&#xff0c;RFC2001&#xff0c;大概在 1990 年代&#xff0c;l…

10分钟内创意爆发,这些头脑风暴技巧让你IDEA满满

当初道叔刚入广告行业&#xff0c;与同事经历了一次困扰的头脑风暴&#xff0c;老板让他们想出一个大集团公司年会的主题口号。我们7-8个团队成员耗费了一个下午的时间&#xff0c;提出了几十个提议&#xff0c;但最终硬是没有一个能满足需求。许多人可能也有过道叔这样的经历。…

Js保留树型数据指定层级

自定义一个树型的数据 const tree [{value: 1, label: "1",children: [{value: 11, label: "1-1",children: [{value: 111, label: "1-1-1"}]}]}, {value: 2, label: "2"}]保留指定层级的方法 function keepNodesAtLevel(data, level…

vue源码阅读之Watcher类

我们上次分析vue源码讲的是收集依赖&#xff0c;数据变化之后我们把依赖收集到dep类中&#xff0c;通过这个管理器进行管理。 里面有一个subs数组&#xff0c;用来存放依赖&#xff0c;并且定义了几个实例方法用来依赖进行添加&#xff0c;删除&#xff0c;通过操作。 比如ad…

mysql基础5——mysql主从

文章目录 一、基本了解二、主从原理三、主从复制3.1 从无到有3.1.1 服务器初始化3.1.2 配置主库3.1.3 配置从库3.1.4 效果验证 3.2 从有到无3.2.1 主库全备&#xff0c;并同步到从库3.2.2 配置主库3.2.3 配置从库3.2.4 效果验证 四、数据库运维4.1 几个参数4.2 查看进程列表 一…

【案例实战】高并发业务的多级缓存架构一致性解决方案

我们在高并发的项目中基本上都离不开缓存&#xff0c;那么既然引入缓存&#xff0c;那就会有一个缓存与数据库数据一致性的问题。 首先&#xff0c;我们先来看看高并发项目里面Redis常见的三种缓存读写模式。 Cache Aside 读写分离模式&#xff0c;是最常见的Redis缓存模式&a…

树莓派教程2023

摘要&#xff1a;本教程只适用于最新版树莓派64位系统&#xff08;debian11&#xff09; 树莓派官网&#xff08;之前是org&#xff09;&#xff1a; https://www.raspberrypi.com/ 一.烧录系统 1.官网下载官方烧写器 https://downloads.raspberrypi.org/imager/imager_late…

部署Redis到云服务器

购买某服务商的云服务器 在云服务器中设置TCP端口号 配置云服务器的开放端口 通过telneat测试端口是否可用 配置redis服务器 需要配置一下自己云服务器的ip 保护模式关闭、允许其他远端连接 设置和云服务器开放一致的端口号 打开此项设置redis的连接密码&#xff0c;更加…

一、计算复杂度

补充说明&#xff1a; 1、决定性问题&#xff1a; 一个决定性问题(decision problem)是指其输出&#xff0c;只有“是”或“否”的问题。 一、P问题&#xff08;polynomial time class&#xff09; 1、问题描述&#xff1a; 可于确定型图灵机以多项式时间求解的决定性问题…

STM32学习之keil5环境搭建

1.keil5是开发ARM内核芯片最常用的集成开发环境IDE。STM32开发环境的搭建主要分为两步&#xff1a; 第一步&#xff0c;安装Kile5.到keil官网下载&#xff1a;Keil Embedded Development Tools for Arm, Cortex-M, Cortex-R4, 8051, C166, and 251 processor families. 第二步…