2021年大数据常用语言Scala(三十六):scala高级用法 泛型
Posted Lansonli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年大数据常用语言Scala(三十六):scala高级用法 泛型相关的知识,希望对你有一定的参考价值。
目录
泛型
scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。
scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)
scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)
在scala中,使用方括号来定义类型参数。
定义一个泛型方法
需求:用一个方法来获取任意类型数组的中间的元素
不考虑泛型直接实现(基于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))
}
加入泛型支持
def getMiddle[A](arr:Array[A]) = arr(arr.length / 2)
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4,5)
val arr2 = Array("a", "b", "c", "d", "f")
println(getMiddle[Int](arr1))
println(getMiddle[String](arr2))
// 简写方式
println(getMiddle(arr1))
println(getMiddle(arr2))
}
定义一个泛型类
我们接下来要实现一个Pair类(一对数据)来讲解scala泛型相关的知识点。
Pair类包含两个值,而且两个值的类型不固定。
// 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
// 这个名字可以任意取
class Pair[T, S](val first: T, val second: S)
case class Person(var name:String, val age:Int)
object Pair {
def main(args: Array[String]): Unit = {
val p1 = new Pair[String, Int]("张三", 10)
val p2 = new Pair[String, String]("张三", "1988-02-19")
val p3 = new Pair[Person, Person](Person("张三", 20), Person("李四", 30))
}
}
要定义一个泛型类,直接在类名后面加上方括号,指定要使用的类型参数。上述的T、S都是类型参数,就代表一个类型
指定了类对应的类型参数后,就可以使用这些类型参数来定义变量了
上下界
现在,有一个需求,在Pair类中,我们只想用来保存Person类型的对象,因为我们要添加一个方法,让好友之间能够聊天。例如:
def chat(msg:String) = println(s"${first.name}对${second.name}说: $msg")
但因为,Pair类中根本不知道first有name这个字段,上述代码会报编译错误。
而且,添加了这个方法,就表示Pair类,现在只能支持Person类或者Person的子类的泛型。所以,我们需要给Pair的泛型参数,添加一个上界。
使用<: 类型名表示给类型添加一个上界,表示泛型参数必须要从上界继承。
// 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
// 这个名字可以任意取
class Pair[T <: Person, S <:Person](val first: T, val second: S) {
def chat(msg:String) = println(s"${first.name}对${second.name}说: $msg")
}
class Person(var name:String, val age:Int)
object Pair {
def main(args: Array[String]): Unit = {
val p3 = new Pair(new Person("张三", 20), new Person("李四", 30))
p3.chat("你好啊!")
}
}
接着再提一个需求,Person类有几个子类,分别是Policeman、Superman。
要控制Person只能和Person、Policeman聊天,但是不能和Superman聊天。此时,还需要给泛型添加一个下界。
// 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
// 这个名字可以任意取
class Pair[T <: Person, S >: Policeman <:Person](val first: T, val second: S) {
def chat(msg:String) = println(s"${first.name}对${second.name}说: $msg")
}
class Person(var name:String, val age:Int)
class Policeman(name:String, age:Int) extends Person(name, age)
class Superman(name:String) extends Policeman(name, -1)
object Pair {
def main(args: Array[String]): Unit = {
// 编译错误:第二个参数必须是Person的子类(包括本身)、Policeman的父类(包括本身)
val p3 = new Pair(new Person("张三", 20), new Superman("李四"))
p3.chat("你好啊!")
}
}
U >: T 表示U必须是类型T的父类或本身
S <: T 表示S必须是类型T的子类或本身
协变、逆变、非变
父类对象 可以指向 子类的实例,这是多态
如果是泛型之间呢?
来一个类型转换的问题:
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 编译报错,无法将p1转换为p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
非变
class Pair[T]{},这种情况就是非变(默认),类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系,这种情况和Java是一样的。
协变
class Pair[+T],这种情况是协变。类型B是A的子类型,Pair[B]可以认为是Pair[A]的子类型。这种情况,参数化类型的方向和类型的方向是一致的。
逆变
class Pair[-T],这种情况是逆变。类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型。这种情况,参数化类型的方向和类型的方向是相反的。
示例:
class Super
class Sub extends Super
//非变
class Temp1[A](title: String)
//协变
class Temp2[+A](title: String)
//逆变
class Temp3[-A](title: String)
object Covariance_demo {
def main(args: Array[String]): Unit = {
val a = new Sub()
// 没有问题,Sub是Super的子类
val b:Super = a
// 非变
val t1:Temp1[Sub] = new Temp1[Sub]("测试")
// 报错!默认不允许转换
// val t2:Temp1[Super] = t1
// 协变
val t3:Temp2[Sub] = new Temp2[Sub]("测试")
val t4:Temp2[Super] = t3
// 非变
val t5:Temp3[Super] = new Temp3[Super]("测试")
val t6:Temp3[Sub] = t5
}
}
以上是关于2021年大数据常用语言Scala(三十六):scala高级用法 泛型的主要内容,如果未能解决你的问题,请参考以下文章
2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)
2021年大数据常用语言Scala(三十):scala面向对象 继承(extends)
2021年大数据常用语言Scala(三十三):scala高级用法 模式匹配
2021年大数据常用语言Scala(三十五):scala高级用法 提取器(Extractor)