目录
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的
* */
2 协变、逆变、非变
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]的子类型。参数化类型的方向和类型的方向是相反的
}
}
作者:
链接:https://blog.csdn.net/qq_26442553/article/details/108477483
来源:CSDN
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。