目录
- 泛型
- 上界
- 下界
- 非变
- 斜变
- 逆变
泛型
泛型的意思是 泛指某种具体的数据类型 , 在Scala中, 泛型用 [ 数据类型] 表示. 在实际开发中, 泛型一般是结合数组或者集合来使用的, 除此之外, 泛型的常见用法还有以下三种:
泛型方法
泛型方法指的是 把泛型定义到方法声明上 , 即:该方法的参数类型是由泛型来决定的 . 在调用方法时, 明确具体的数据类型.
格式
需求
定义方法getMiddleElement()
, 用来获取任意类型数组的中间元素.
- 思路一 : 不考虑泛型直接实现(基于Array[Int]实现)
- 思路二 : 加入泛型支持.
参考代码
def main(args: Array[String]): Unit = { val arr1 = Array(1, 2, 3, 4, 5, 6) val arr2 = Array("a", "b", "c", "d", "e", "f")
println(getMiddleElement(arr1)) println(getMiddleElement(arr2)) }
def getMiddleElement[T](arr: Array[T]): T = { arr(arr.length / 2) }
|
泛型类
泛型类指的是 把泛型定义到类的声明上 , 即:该类中的成员的参数类型是由泛型来决定的 . 在创建对象时, 明确具体的数据类型.
格式
需求
- 定义一个泛型类, 该类包含两个字段,且两个字段的类型不固定.
- 创建不同类型的泛型类对象,并打印.
参考代码
class GenericClass[T] (val a:T, val b:T){
def printElement(ele:T): Unit ={ println(ele) } }
object GenericClass{ def main(args: Array[String]): Unit = {
val generic1 = new GenericClass[String]("zhangsan", "喜欢女孩") val generic2= new GenericClass[Int](18, 10)
println(generic1.a) generic1.printElement("hehe") println(generic2.a) generic2.printElement(15) } }
|
泛型特质
泛型特质指的是 把泛型定义到特质的声明上 , 即:该特质中的成员的参数类型是由泛型来决定的 . 在定义泛型特质的子类或者子单例对象时, 明确具体的数据类型.
格式
trait 特质A[T] { } class 类B extends 特质A[指定具体的数据类型] { } 123456
|
需求
- 定义泛型特质, 该类有一个变量a和show()方法, 它们都是用特质的泛型.
- 定义单例对象, 继承特质.
- 打印单例对象中的成员.
参考代码
trait GenericTrait[T] { def show(a:T): Unit = println(a) }
class GenericTraitA[E] extends GenericTrait[E] {}
class GenericTraitB extends GenericTrait[Int] {}
object GenericTrait { def main(args: Array[String]): Unit = { val a = new GenericTraitA[String] a.show("zs")
val b = new GenericTraitB b.show(1)
} }
|
上下界
我们在使用泛型(方法, 类, 特质)时,如果要限定该泛型必须从哪个类继承、或者必须是哪个类的父类。此时,就需要使用到 泛型的上下界 。
上界
使用 T <: 类型名 表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承.
格式
例如: [T <: Person]的意思是, 泛型T的数据类型必须是Person类型或者Person的子类型
需求
- 定义一个Person类
- 定义一个Student类,继承Person类
- 定义一个泛型方法demo(),该方法接收一个Array参数.
- 限定demo方法的Array元素类型只能是Person或者Person的子类
- 测试调用demo()方法,传入不同元素类型的Array
参考代码
class Person(val name:String){ override def toString: String = name }
class Student(override val name: String) extends Person(name){ override def toString: String = name }
object UpperBound {
def printlnArr[T <: Person](arr:Array[T]): Unit = arr.foreach(x => println(x.toString))
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4) val arr2 = Array(new Student("zs"),new Student("ls"),new Student("ww"))
printlnArr(arr2)
} }
|
下界
使用 T >: 数据类型 表示给类型添加一个下界,表示泛型参数必须是从该类型本身或该类型的父类型.
格式
注意:
- 例如: [T >: Person]的意思是, 泛型T的数据类型必须是Person类型或者Person的父类型
- 如果泛型既有上界、又有下界。下界写在前面,上界写在后面. 即: [T >: 类型1 <: 类型2]
需求
- 定义一个PersonMan类
- 定义一个PoliceMan类,继承PersonMan类
- 定义一个SuperMan类,继承PoliceMan类
- 定义一个demo泛型方法,该方法接收一个Array参数,
- 限定demo方法的Array元素类型只能是PersonMan、PersonMan
- 测试调用demo,传入不同元素类型的Array
参考代码
class PersonMan(val name:String){ override def toString: String = name }
class PoliceMan(override val name: String) extends PersonMan(name){ override def toString: String = name }
class SuperMan(override val name: String) extends PoliceMan(name){ override def toString: String = name }
object DownBound {
def printlnArr[T >: PoliceMan](array: Array[T]): Unit = array.foreach(x => println(x.toString))
def main(args: Array[String]): Unit = {
val arr1 = Array(new SuperMan("z3"),new SuperMan("l4"),new SuperMan("w5")) val arr2 = Array(new PersonMan("z3"),new PersonMan("l4"),new PersonMan("w5"))
printlnArr(arr2)
} }
|
型变
在Spark的源代码中大量使用到了协变、逆变、非变,学习该知识点对我们将来阅读spark源代码很有帮助。
- 非变 : 类A和类B之间是父子类关系, 但是Pair[A]和Pair[B]之间没有 任何关系 .
- 协变 : 类A和类B之间是父子类关系, Pair[A]和Pair[B]之间也有 父子类 关系.
- 逆变 : 类A和类B之间是父子类关系, 但是Pair[A]和Pair[B]之间是 子父类 关系.
如下图:
非变
语法格式
- 默认泛型类是 非变的
- 即 : 类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
斜变
语法格式
- 类型 B是A的子类型,Pair[B]可以认为是Pair[A]的子类型
- 参数化类型的方向和类型的方向是一致的。
逆变
语法格式
- 类型 B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
- 参数化类型的方向和类型的方向是相反的
示例
需求
- 定义一个Super类、以及一个Sub类继承自Super类
- 使用协变、逆变、非变分别定义三个泛型类
- 分别创建泛型类对象来演示协变、逆变、非变
参考代码
class Super()
// 子类 class Sub() extends Super()
// 协变 : 类A和类B之间是父子类关系, Pair[A]和Pair[B]之间也有 父子 类关系. class Covariant[+T](val name: String)
// 逆变 : 类A和类B之间是父子类关系, 但是Pair[A]和Pair[B]之间是 子父 类关系. class Contravariant[-T](val name: String)
// 非变 : 类A和类B之间是父子类关系, 但是Pair[A]和Pair[B]之间 没有任何 关系 . class Invariant[T]()
object TypeChange {
def main(args: Array[String]): Unit = { val invSub = new Invariant[Sub]()
val covSub = new Covariant[Sub]("zs") val covSuper: Covariant[Super] = covSub println(covSuper.name)
val conSub = new Contravariant[Sub]("zs")
val conSub2 = new Contravariant[Super]("zs") val conSuper2: Contravariant[Sub] = conSub2 println(conSuper2.name)
} }
|