Scala系列22:Scala泛型,非变,协变,逆变等使用详解

2020-09-28 20:00发布

目录

0.泛型

0.1定义一个泛型方法

1.泛型类

1.1 泛型的上下界

1.1.1 泛型|上界

1.1.2 泛型|下界

2 协变、逆变、非变

2.1 非变

2.2 协变

2.3 逆变

2.4 示例说明


0.泛型

scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。所以泛型,简单点理解就是定义(类,方法等)不指定具体数据类型,而在使用时才传入具体使用类型,这样的好处就是支持多类型的复用。

0.1定义一个泛型方法

在scala中,使用方括号[]来定义类型参数。

语法格式

  1. def 方法名[泛型名称](..) = {

  2. //...

  3. }

示例说明

  • 用一个方法来获取任意类型数组的中间的元素

    • 不考虑泛型直接实现(基于Array[Int]实现)

    • 加入泛型支持

参考代码

不考虑泛型的实现,指定方法的传入数据类型。

  1. def getMiddle(arr:Array[Int]) = arr(arr.length / 2)


  2. def main(args: Array[String]): Unit = {

  3. val arr1 = Array(1,2,3,4,5)


  4. println(getMiddle(arr1)) //3

  5. }

加入泛型支持,该方法可以传入任意数据类型的数组参数

  1. object _08ObjectDemo {

  2. def getMiddleElement[T](array:Array[T]) = array(array.length / 2)


  3. def main(args: Array[String]): Unit ={

  4. println(getMiddleElement(Array(1, 2, 3, 4, 5))) //3

  5. println(getMiddleElement(Array(1, 2, 3, 4, 5,6))) //4

  6. println(getMiddleElement(Array("a", "b", "c", "d", "e"))) //c

  7. }

  8. }

1.泛型类

scala的类也可以定义泛型。接下来,我们来学习如何定义scala的泛型类

语法格式

class 类[T](val 变量名: T)

尖叫提示:

  • 定义一个泛型类,直接在类名后面加上方括号,指定要使用的泛型参数

  • 指定类对应的泛型参数后,就使用这些类型参数来定义变量了

示例说明

  • 实现一个Pair泛型类

  • Pair类包含两个字段,而且两个字段的类型不固定

  • 创建不同类型泛型类对象,并打印

参考代码

  1. case class Pair[T](var a:T, var b:T)


  2. def main(args: Array[String]): Unit = {

  3. val pairList = List(

  4. Pair("Hadoop", "Storm"),

  5. Pair("Hadoop", 2008),

  6. Pair(1.0, 2.0),

  7. Pair("Hadoop", Some(1.9))

  8. )


  9. println(pairList)

  10. }

  11. //List(Pair(Hadoop,Storm), Pair(Hadoop,2008), Pair(1.0,2.0), Pair(Hadoop,Some(1.9)))

1.1 泛型的上下界

需求:我们在定义方法/类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时,就需要使用到上下界。

1.1.1 泛型|上界

使用<: 类型名表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承。

[T <: 类型]

示例说明

  • 定义一个Person类

  • 定义一个Student类,继承Person类

  • 定义一个demo泛型方法,该方法接收一个Array参数,

  • 限定demo方法的Array元素类型只能是Person或者Person的子类

  • 测试调用demo,传入不同元素类型的Array

参考代码

  1. class Person

  2. class Student extends Person


  3. def demo[T <: Person](a:Array[T]) = println(a)


  4. def main(args: Array[String]): Unit = {

  5. demo(Array(new Person))

  6. demo(Array(new Student))

  7. // 编译出错,必须是Person的子类

  8. // demo(Array("hadoop"))

  9. }

1.1.2 泛型|下界

  上界是要求必须是某个类的子类,或者必须从某个类继承,而下界是必须是某个类的父类(或本身)

语法格式

[T >: 类型]

注意:如果类既有上界、又有下界。下界写在前面,上界写在后面,如下:

def demo[T >: Policeman <: Person ](array:Array[T]) = println(array)

示例说明

  • 定义一个Person类

  • 定义一个Policeman类,继承Person类

  • 定义一个Superman类,继承Policeman类

  • 定义一个demo泛型方法,该方法接收一个Array参数,

  • 限定demo方法的Array元素类型只能是Person、Policeman

  • 测试调用demo,传入不同元素类型的Array

参考代码

  1. package com.robot.scalademo


  2. class Person

  3. class Policeman extends Person

  4. class Superman extends Policeman


  5. object _08ObjectDemo {


  6. def demo[T >: Policeman](array:Array[T]) = println(array)


  7. def main(args: Array[String]): Unit = {

  8. demo(Array(new Person))

  9. demo(Array(new Policeman))

  10. // 编译出错:Superman是Policeman的子类

  11. // demo(Array(new Superman))

  12. }

  13. }

  14. /**

  15. demo(Array(new Person))

  16. demo(Array(new Policeman))

  17. // 编译出错:Superman是Policeman的

  18. * */


  19. ---------------------------------------------------------------------

  20. //泛型既有上界也有下界

  21. package com.robot.scalademo

  22. class Human

  23. class Person extends Human

  24. class Policeman extends Person

  25. class Superman extends Policeman


  26. object _08ObjectDemo {


  27. def demo[T >: Policeman <: Person ](array:Array[T]) = println(array)


  28. def main(args: Array[String]): Unit = {

  29. demo(Array(new Person))

  30. demo(Array(new Policeman))

  31. // 编译出错:Superman是Policeman的子类

  32. // demo(Array(new Superman))

  33. // 编译出错:Human是Person的父类

  34. //demo(Array(new Human))

  35. }

  36. }

  37. /**

  38. demo(Array(new Person))

  39. demo(Array(new Policeman))

  40. // 编译出错:Superman是Policeman的

  41. * */

协变、逆变、非变

spark的源代码中大量使用到了协变、逆变、非变,学习该知识点对我们将来阅读spark源代码很有帮助。如下,如何让带有泛型的类支持类型转换呢?

  1. class Pair[T]


  2. object Pair {

  3. def main(args: Array[String]): Unit = {

  4. val p1 = Pair("hello")

  5. // 编译报错,无法将p1转换为p2

  6. val p2:Pair[AnyRef] = p1 // AnyRef是所有其他数据类型的父类


  7. println(p2)

  8. }

  9. }

关于非变,协变,逆变与原类型之间的关系,下面一张图概况之。 

2.1 非变

语法格式:

class Pair[T]{}

  • 默认泛型类是非变的

  • 类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系

  • Java是一样的

2.2 协变

语法格式

class Pair[+T]

  • 类型B是A的子类型,Pair[B]可以认为是Pair[A]的子类型

  • 参数化类型的方向和类型的方向是一致的。

2.3 逆变

语法格式

class Pair[-T]

  • 类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型

  • 参数化类型的方向和类型的方向是相反的

2.4 示例说明

  • 定义一个Super类、以及一个Sub类继承自Super类

  • 使用协变、逆变、非变分别定义三个泛型类

  • 分别创建泛型类来演示协变、逆变、非变

参考代码

  1. object _08ObjectDemo {

  2. def main(args: Array[String]): Unit = {

  3. //非变案例演示

  4. val A:Temp1[Sub] = new Temp1[Sub]

  5. //val B:Temp1[Super] = A //非变, 编译报错,不能赋值。

  6. //尽管类型A是B的子类型,Pair[A]和Pair[B]没有任何从属关系,参数化类型之间没有关系,不管原类型之间的关系


  7. // 协变案例演示

  8. val C: Temp2[Sub] = new Temp2[Sub]

  9. val D: Temp2[Super] = C

  10. //类型C是D的子类型,Pair[C]可以认为是Pair[D]的子类型,参数化类型的方向和类型的方向是一致的。


  11. // 逆变案例演示

  12. val E: Temp3[Super] = new Temp3[Super]

  13. val F: Temp3[Sub] = E

  14. //类型F是E的子类型,Pair[E]反过来可以认为是Pair[F]的子类型。参数化类型的方向和类型的方向是相反的

  15. }

  16. }

作者:涤生手记

链接:https://blog.csdn.net/qq_26442553/article/details/108477483

来源:CSDN
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。