【图文详细 】Scala——泛型_scala泛型可以加减乘除吗-程序员宅基地

技术标签: Scala  Scala泛型  

3、Scala 泛型 

 

3.1、Scala 泛型基础 

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

泛型方法:指定方法可以接受任意类型参数。 

package com.mazh.scala.day3 
 
class Person[T](var name:T) 
class Student[T,S](name:T,var age:S) extends Person(name) 
 
/** 
  * 作者: 李涛  https://blog.csdn.net/qq_42246689 
  */ 
object GenericTypeTest { 
  def main(args: Array[String]): Unit = { 
    println (new Student[String,Int]("黄渤",33).name) 
  } 
} 
 

 

3.2、Scala 类型变量界定 

类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩小泛型的具体

范围 比如下面的代码编译不通过:

class GenericTypeTest2 { 
  def compare[T](first:T,second:T) = { 
    if (first.compareTo(second)>0) 
      first 
    else 
      second 
  } 
} 
 
object GenericTypeTest2{ 
  def main(args: Array[String]): Unit = { 
    val tvb = new GenericTypeTest2 
    println (tvb.compare("A", "B")) 
  } 
} 

代码为什么编译不通过,是因为:泛型 T 并不一定具备 compareTo 方法 
 

如果想编译通过,请做如下更改: 

class GenericTypeTest2 { 
  def compare[T <: Comparable[T]](first:T,second:T)={ 
    if (first.compareTo(second)>0) 
      first 
    else 
      second 
  } 
} 
 
object GenericTypeTest2{ 
  def main(args: Array[String]): Unit = { 
    val tvb=new GenericTypeTest2 
    println (tvb.compare("A", "B")) 
 } 
} 

代码中改动的地方:

T <: Comparable[T]

这是什么意思呢?

compareTo 方法中如果输入的类型处于 Comparable 类对应继承层次结构中,则是合法的, 否则的话编译会报错 

 

3.3、Scala 视图界定

上面讲的类型变量界定建立在类继承层次结构的基础上,但有时候这种限定不能满足实际要 求,如果希望跨越类继承层次结构时,可以使用视图界定来实现的,其后面的原理是通过隐 式转换来实现。 
 
隐含参数和方法也可以定义隐式转换,称作视图。视图的绑定从另一个角度看就是 implicit 的转换。主要用在两个场合: 、当一个 T 类型的变量 t 要装换成 A 类型时 、当一个类型 T 的变量 t 无法拥有 A 类型的 a 方法或变量时  其实视图的绑定是为了更方便的使用隐式装换 视图界定利用<%符号来实现 
 
如下:代码的执行会抛出异常

object GenericTypeTest3{ 
 
  def main(args: Array[String]): Unit = { 
    // 这是合法的语句 
    val s= Student11 ("john","170") 
    // 下面这条语句不合法,这是因为 ,Int 类型没有实现 Comparable 接口 
    val s2= Student11 ("john",170) 
  } 
} 

如果想通过,那么要使用视图界定的技术 更改之后的代码: 

package com.mazh.scala.day3 
 
case class Student11[T,S <% Comparable[S]](var name:T,var height:S) 
object GenericTypeTest3{ 
 
  def main(args: Array[String]): Unit = { 
    // 这是合法的语句 
    val s= Student11 ("john","170") 
    // Int 类型 的变量经过 隐式 转换成了 R ichInt 类型, R ichInt 类型是实现了 C omparable 接口的 
    val s2= Student11 ("john",170) 
  } 
} 

改动的代码:

case class Student11[T, S <% Comparable[S]](var name:T, var height:S) 
 
能运行的原因:

利用<%符号对泛型 S 进行限定,它的意思是 S 可以是 Comparable 类继承层次结构中实现了 Comparable 接口的类,也可以是能够经过隐式转换得到的实现了 Comparable 接口的类。 
 
上面改动的语句在视图界定中是合法的,因为 Int 类型此时会隐式转换为 RichInt 类,而 RichInt 类属于 Comparable 继承层次结构。Int 类会隐式转换成 RichInt 类,RichInt 并不是直 接实现 Comparable 口,而是通过 ScalaNumberProxy 类将 Comparable 中的方法继承过来。 

 

3.4、Scala 上界下界 

上界、下界介绍  在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型。比如,要求某个 泛型类型,必须是某个类的子类,这样在程序中就可以放心的调用父类的方法,程序才能正 常的使用与运行。此时,就可以使用上下边界 Bounds 的特性;  Scala 的上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类; 
1、U >: T 这是类型下界的定义,也就是 U 必须是类型 T 的父类(或本身,自己也可以认为是自己的父 类)。 
2、S <: T 这是类型上界的定义,也就是 S 必须是类型 T 的子类(或本身,自己也可以认为是自己的子 类)。 

3.4.1、上界 
在讲解类型变量绑定的内容中,咱们写过这么一段代码:

class GenericTypeTest2 { 
  def compare[T <: Comparable[T]](first:T,second:T)={ 
    if (first.compareTo(second)>0) 
      first 
    else 
      second 
  } }

标红的地方,其实就是上界,因为它限定了继承层次结构中最顶层的类,例如 T <: Comparable[T] 表示泛型 T 的类型的最顶层类是 Comparable,所有输入是 Comparable 的子类都是合法的,其 它的都是非法的,因为被称为上界 

 

3.4.2、下界 

当然,除了上界之外,还有个非常重要的内容就是下界,下界通过>:符号来标识,比如: 

class A 
class B extends A 
class C extends B 
class D extends C 

如果代码中这么写:opt[T >: C]

那么表示 T 的类型只能是 A,B,C 了。不能是 D,其实就是限制了最底层的类型是什么。在类 的继承结构体系中,从上到下,只能到类型 C 为止 
 
下界的作用主要是保证类型安全 
 
实例代码:

object GenericTypeTest4 { 
 
  def getIDCard[R >: Son](person:R): Unit ={
     println("好吧,他的身份证就交给你保管了"); 
  } 
 
  def main(args: Array[String]): Unit = { 
 
// getIDCard [ T ] (t:T) 前面 这个 T 表示方法中 的参数 类型 被固定下来。 
// 在 定义 的时候还不知道这个 T 类型 到底应该是什么,但是 调用 的时候,被确定 下来 是某种类型 
// 但是 , 参数 中的类型,无论如何都可以是 T 的子类 类型,这属于多态范畴
 
    getIDCard[GranderFather](new GranderFather) 
    getIDCard[GranderFather](new Father) 
    getIDCard[Father](new Father) 
    getIDCard[Son](new Son) 
 
    // 这句代码会报错
     getIDCard[Tongzhuo](new Tongzhuo) 
  } 
} 
 
class GranderFather 
class Father extends GranderFather 
class Son extends Father 
class Tongzhuo

 

3.5、Scala 逆变和协变 


3.5.1、协变 
协变定义形式如:

trait List[+T]{}

当类型 B 是类型 A 的子类型时,则 List[B]也可以认为是 List[A}的子类型,即 List[B]可以泛化 为 List[A]。

也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变 (covariance) 

首先,Java 中不存在协变: 

public class TypeTest { 
  
 public static void main(String[] args) { 
   
  java.util.List<String> s1 = new LinkedList<String>(); 
  java.util.List<Object> s2 = new LinkedList<Object>(); 
  /**
   * 下面这条语句会报错 
   * Type mismatch: cannot convert from List<String> to List<Object> 
   */ 
  s2 = s1; 
 } 
} 

然在类层次结构上看,String 是 Object 类的子类,但 List<String>并不是的 List<Object>子类, 也就是说它不是协变的。Java 的灵活性就这么差吗?其实 Java 不提供协变和逆变这种特性 是有其道理的,这是因为协变和逆变会破坏类型安全。假设上面的代码是合法的,我们此时 完全可以 s2.add(new Person(“xuzheng”)往集合中添加 Person 对象,但此时我们知道,s2 已 经指向了 s1,而 s1 里面的元素类型是 String 类型,这时其类型安全就被破坏了,从这个角 度来看,Java 不提供协变和逆变是有其合理性的。 
 
那为什么 Java 不支持,而 Scala 支持呢? 
 
先来看一段 Scala 代码:Scala 比 Java 更灵活,当不指定逆变和协变时,和 Java 是一样的。 代码如下:
 

package com.mazh.scala.day3.gao 
 
object XieBianTest { 
  def main(args: Array[String]): Unit = {
     val list1:MyList[String]= new MyList[String]("黄",null)
     val list2:MyList[String]= new MyList[String]("黄",new MyList[String]("黄",null)) 
  } 
} 
class MyList[T](val head: T, val tail: MyList[T]){} 

3.5.2、逆变 
逆变定义形式如:trait List[-T]{} 当类型 B 是类型 A 的子类型,

则 Queue[A]反过来可以认为是 Queue[B}的子类型。

也就是被 参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance) 
 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42246689/article/details/85231848

智能推荐

buck dcm占空比计算_一看就会!这位牛人把MOS开关损耗计算写神了-程序员宅基地

文章浏览阅读643次。电源工程师们都知道开关MOS在整个电源系统里面的损耗占比是不小的,我们谈及最多的就是开通损耗和关断损耗,由于这两个损耗不像导通损耗或驱动损耗一样那么直观,所以有部分人对于它计算还有些迷茫。今天我们就来详细分析计算一下开关损耗,并论述实际状态下功率MOSFET的开通过程和自然零电压关断的过程,从而使电子工程师知道哪个参数起主导作用并更加深入理解MOSFET。MOSFET开关损耗1、开通过程..._buck电路电感和开关管损耗占比

cocos2dx——裁剪节点ClippingNode-程序员宅基地

文章浏览阅读77次。【唠叨】 学习cocos2dx 3.2确实比较吃力,因为网上关于最新版的v3.2的资料十分稀少,或者是讲解的确实不是很详细。大部分人都是根据官方文档照样画瓢,而对于有些比较抽象的概念及函数都是照着官方文档来讲解的。这样的结果,导致有些东西令我确实非常费解。没有办法,只好自己来总结cocos2dx3.2,然后将个人的学习感悟分享给大家。PS:当然有些大牛写的文章还是..._clippingnode 性能

Viper炫彩蛇的下载与安装(MSF图形化界面)_简易极速安装版-程序员宅基地

文章浏览阅读647次,点赞2次,收藏2次。1.更新apt-get2.下载docker.ioY。_炫彩蛇

CVPR2021|| Coordinate Attention注意力机制_位置注意力机制-程序员宅基地

文章浏览阅读2.4w次,点赞63次,收藏418次。Paper:https://arxiv.org/pdf/2103.02907.pdfGitHub:https://github.com/Andrew-Qibin/CoordAttention轻量,优秀,好用!讲之前我们先回顾一下以前的SE与CBAMSESE比较简单,看一下结构图差不多就能理解了,如果有些实现不太懂的,可以借鉴一下CBAM的。但SE只考虑内部通道信息而忽略了位置信息的重要性,而视觉中目标的空间结构是很重要的。CBAM稍微介绍一下CBAM,如图b所示,CBAM包含空间注意力和通_位置注意力机制

eslint搭配prettier使用步骤_plugin:prettier/recommended-程序员宅基地

文章浏览阅读1.2k次。这里的执行逻辑顺序是:eslint会首先读extends的规则,这个时候遇到了最后配置的plugin:prettier/recommended,而这个插件又会先读本地配置的.prettierrc文件再读取prettier自己内部设置的配置,最后读.eslintrc.json的rules配置。2、其次配置.eslintrc.json文件,extends: [“plugin:prettier/recommended”],如果有其他扩展,则"plugin:prettier/recommended"放在最后。_plugin:prettier/recommended

kettle 共享数据库连接时带汉字引发的错误-程序员宅基地

文章浏览阅读548次。处理方式:在目录: C:\Users\Administrator\.kettle 里 找到:shared.xml 把该文件的编码方式改为UTF-8,重启kettle就没问题了

随便推点

HDOJ 2544 BellmanFord实现-程序员宅基地

文章浏览阅读69次。1 #include <cstdio> 2 3 struct road{ 4 int head; 5 int tail; 6 int value; 7 }; 8 9 #define Infinity 23333333310 11 int main(){12 int n, m;13 ...

大数据Hadoop复习笔记_燕山大学大数据期末复习笔记-程序员宅基地

文章浏览阅读3.9k次,点赞14次,收藏39次。题型与分值选择题10道*2分=20分填空题5道*2分=10分判断题5道*1分=5分简答题2道*10分=20分方案设计题1道*15分=15分(无唯一标准答案,可用中文写清楚每个关键步骤和重要技术点,也可直接写代码,或者中文和代码混合说明)注意:①写清方案实行的步骤②每个步骤的重要技术点,比如用的哪个类来实现程序设计题6段*5分=30分(从挖行改成挖段)内容复习hive的JSON和多字节分隔符的解析步骤和关键技术点复习hive窗口函数的使用方法和含义集群与分布式分布式_燕山大学大数据期末复习笔记

java读取TXT文件类_readtxt.readfile java-程序员宅基地

文章浏览阅读431次。Java读取txt文件内容。可以作如下理解:首先获得一个文件句柄。File file = new File();file即为文件句柄。两人之间连通电话网络了。接下来可以开始打电话了。通过这条线路读取甲方的信息:new FileInputStream(file)目前这个信息已经读进来内存当中了。接下来需要解读成乙方可以理解的东西既然你使用了FileInputStream(_readtxt.readfile java

天才ACM-程序员宅基地

文章浏览阅读446次,点赞11次,收藏7次。给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下:从集合 S 中取出 M 对数(即 2×M 个数,不能重复使用集合中的数,如果 S 中的整数不够 M 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值就称为集合 S 的“校验值”。现在给定一个长度为 N 的数列 A 以及一个整数 T。我们要把 A 分成若干段,使得每一段的“校验值”都不超过 T。求最少需要分成几段。_天才acm

仅用5个线程,让Idea全系列Ide能看电视、直播、电影、听广播、音乐、美女图_bg-boom-程序员宅基地

文章浏览阅读7.6k次,点赞21次,收藏24次。今天要分享的技术方案是我在写bg-boom这款插件当中一小块的代码实现方案,先从需求说起吧,其模块整体的需求是丰富idea的背景功能,让背景支持电影、视频、直播、TV、广播、音乐、美女图等功能。_bg-boom

联想适合计算机专业的游戏本,全球最佳游戏笔记本电脑Top 10-程序员宅基地

文章浏览阅读751次。如果你告诉你的朋友你想买一台电脑,他们肯定会建议你买一台台式电脑。因为在相同的预算下,台式电脑可以比笔记本电脑带来更强大的性能。然而,在很多情况下,台式电脑的不可移动性会给游戏玩家带来很多麻烦,这并不像笔记本电脑那样方便。Origin EON15-X:桌面级的CPU造就一个无与伦比的游戏笔记本电脑处理器:4GHz英特尔酷睿i7-4790K;显卡:英伟达GeForce GTX 980 M (..._联想买什么电脑好,能玩游戏,办公软件