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

目录

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中,使用方括号[]来定义类型参数。

语法格式

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

示例说明

  • 用一个方法来获取任意类型数组的中间的元素
    • 不考虑泛型直接实现(基于Array[Int]实现)
    • 加入泛型支持

参考代码

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

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

  def main(args: Array[String]): Unit = {
    val arr1 = Array(1,2,3,4,5)

    println(getMiddle(arr1))  //3
  }

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

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

  def main(args: Array[String]): Unit ={
    println(getMiddleElement(Array(1, 2, 3, 4, 5))) //3
    println(getMiddleElement(Array(1, 2, 3, 4, 5,6))) //4
    println(getMiddleElement(Array("a", "b", "c", "d", "e"))) //c
  }
}

1.泛型类

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

语法格式

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

尖叫提示:

  • 定义一个泛型类,直接在类名后面加上方括号,指定要使用的泛型参数
  • 指定类对应的泛型参数后,就使用这些类型参数来定义变量了

示例说明

  • 实现一个Pair泛型类
  • Pair类包含两个字段,而且两个字段的类型不固定
  • 创建不同类型泛型类对象,并打印

参考代码

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

def main(args: Array[String]): Unit = {
    val pairList = List(
        Pair("Hadoop", "Storm"), 
        Pair("Hadoop", 2008),
        Pair(1.0, 2.0),
        Pair("Hadoop", Some(1.9))
    )

    println(pairList)
}
//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

参考代码

class Person
class Student extends Person

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

def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Student))
    // 编译出错,必须是Person的子类
    // demo(Array("hadoop"))
}

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

参考代码

package com.robot.scalademo

class Person
class Policeman extends Person
class Superman extends Policeman

object _08ObjectDemo {

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

  def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的子类
    // demo(Array(new Superman))
  }
}
/**
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的
  * */

---------------------------------------------------------------------
//泛型既有上界也有下界
package com.robot.scalademo
class Human
class Person  extends  Human
class Policeman extends Person
class Superman extends Policeman

object _08ObjectDemo {

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

  def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的子类
    // demo(Array(new Superman))
    // 编译出错:Human是Person的父类
    //demo(Array(new Human))
  }
}
/**
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的
  * */

协变、逆变、非变

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

class Pair[T]

object Pair {
  def main(args: Array[String]): Unit = {
    val p1 = Pair("hello")
    // 编译报错,无法将p1转换为p2
    val p2:Pair[AnyRef] = p1  // AnyRef是所有其他数据类型的父类

    println(p2)
  }
}

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

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类
  • 使用协变、逆变、非变分别定义三个泛型类
  • 分别创建泛型类来演示协变、逆变、非变

参考代码

object _08ObjectDemo {
  def main(args: Array[String]): Unit = {
    //非变案例演示
    val A:Temp1[Sub] = new Temp1[Sub]
    //val B:Temp1[Super] = A      //非变, 编译报错,不能赋值。
    //尽管类型A是B的子类型,Pair[A]和Pair[B]没有任何从属关系,参数化类型之间没有关系,不管原类型之间的关系

    // 协变案例演示
    val C: Temp2[Sub] = new Temp2[Sub]
    val D: Temp2[Super] = C
    //类型C是D的子类型,Pair[C]可以认为是Pair[D]的子类型,参数化类型的方向和类型的方向是一致的。

    // 逆变案例演示
    val E: Temp3[Super] = new Temp3[Super]
    val F: Temp3[Sub] = E
   //类型F是E的子类型,Pair[E]反过来可以认为是Pair[F]的子类型。参数化类型的方向和类型的方向是相反的
  }
}

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页