2021年大数据常用语言Scala(三十六):scala高级用法 泛型

举报
Lansonli 发表于 2021/09/29 00:43:03 2021/09/29
【摘要】 目录 泛型 定义一个泛型方法 定义一个泛型类 上下界 协变、逆变、非变  非变 协变 逆变 泛型 scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。 scala> val list1:List[String] =&nbs...

目录

泛型

定义一个泛型方法

定义一个泛型类

上下界

协变、逆变、非变 

非变

协变

逆变


泛型

scala和Java一样,类和特质、方法都可以支持泛型。我们在学习集合的时候,一般都会涉及到泛型。


  
  1. scala> val list1:List[String] = List("1""2""3")
  2. list1: List[String] = List(123)
  3. scala> val list1:List[String] = List("1""2""3")
  4. list1: List[String] = List(123)

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

定义一个泛型方法

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

不考虑泛型直接实现(基于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))
  5.   }

 

加入泛型支持


  
  1.   def getMiddle[A](arr:Array[A]) = arr(arr.length / 2)
  2.   def main(args: Array[String]): Unit = {
  3.     val arr1 = Array(1,2,3,4,5)
  4.     val arr2 = Array("a", "b", "c", "d", "f")
  5.     println(getMiddle[Int](arr1))
  6.     println(getMiddle[String](arr2))
  7.     // 简写方式
  8.     println(getMiddle(arr1))
  9.     println(getMiddle(arr2))
  10.   }

 

定义一个泛型类

我们接下来要实现一个Pair类(一对数据)来讲解scala泛型相关的知识点。

Pair类包含两个值,而且两个值的类型不固定。

// 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
// 这个名字可以任意取


  
  1. class Pair[T, S](val first: T, val second: S
  2. case class Person(var name:String, val age:Int)
  3. object Pair {
  4.   def main(args: Array[String]): Unit = {
  5.     val p1 = new Pair[String, Int]("张三", 10)
  6.     val p2 = new Pair[String, String]("张三", "1988-02-19")
  7.     val p3 = new Pair[Person, Person](Person("张三", 20), Person("李四", 30))
  8.   }
  9. }

要定义一个泛型类,直接在类名后面加上方括号,指定要使用的类型参数。上述的T、S都是类型参数,就代表一个类型

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

 

上下界

现在,有一个需求,在Pair类中,我们只想用来保存Person类型的对象,因为我们要添加一个方法,让好友之间能够聊天。例如:

def chat(msg:String) = println(s"${first.name}对${second.name}说: $msg")

但因为,Pair类中根本不知道first有name这个字段,上述代码会报编译错误。

而且,添加了这个方法,就表示Pair类,现在只能支持Person类或者Person的子类的泛型。所以,我们需要给Pair的泛型参数,添加一个上界。

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


  
  1. // 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
  2. // 这个名字可以任意取
  3. class Pair[T <: PersonS <:Person](val first: T, val second: S) {
  4.   def chat(msg:String) = println(s"${first.name}${second.name}说: $msg")
  5. }
  6. class Person(var name:String, val age:Int)
  7. object Pair {
  8.   def main(args: Array[String]): Unit = {
  9.     val p3 = new Pair(new Person("张三"20), new Person("李四"30))
  10.     p3.chat("你好啊!")
  11.   }
  12. }

 

接着再提一个需求,Person类有几个子类,分别是Policeman、Superman。

 

要控制Person只能和Person、Policeman聊天,但是不能和Superman聊天。此时,还需要给泛型添加一个下界。


  
  1. // 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
  2. // 这个名字可以任意取
  3. class Pair[T <: PersonS >: Policeman <:Person](val first: T, val second: S) {
  4.   def chat(msg:String) = println(s"${first.name}${second.name}说: $msg")
  5. }
  6. class Person(var name:String, val age:Int)
  7. class Policeman(name:String, age:Int) extends Person(name, age)
  8. class Superman(name:String) extends Policeman(name, -1)
  9. object Pair {
  10.   def main(args: Array[String]): Unit = {
  11. // 编译错误:第二个参数必须是Person的子类(包括本身)、Policeman的父类(包括本身)
  12.     val p3 = new Pair(new Person("张三"20), new Superman("李四"))
  13.     p3.chat("你好啊!")
  14.   }
  15. }

U >: T 表示U必须是类型T的父类或本身

S <: T 表示S必须是类型T的子类或本身

 

协变、逆变、非变 

 

父类对象 可以指向 子类的实例,这是多态

如果是泛型之间呢?

来一个类型转换的问题:


  
  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
  7.     println(p2)
  8.   }
  9. }

 

非变

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]的子类型。这种情况,参数化类型的方向和类型的方向是相反的。

 

示例:


  
  1. class Super
  2. class Sub extends Super
  3. //非变
  4. class Temp1[A](title: String)
  5. //协变
  6. class Temp2[+A](title: String)
  7. //逆变
  8. class Temp3[-A](title: String)
  9. object Covariance_demo {
  10.   def main(args: Array[String]): Unit = {
  11.     val a = new Sub()
  12.     // 没有问题,Sub是Super的子类
  13.     val b:Super = a
  14.     // 非变
  15.     val t1:Temp1[Sub] = new Temp1[Sub]("测试")
  16.     // 报错!默认不允许转换
  17.     // val t2:Temp1[Super] = t1
  18.     // 协变
  19.     val t3:Temp2[Sub] = new Temp2[Sub]("测试")
  20.     val t4:Temp2[Super] = t3
  21.     
  22.     // 非变
  23.     val t5:Temp3[Super] = new Temp3[Super]("测试")
  24.     val t6:Temp3[Sub] = t5
  25.   }
  26. }

 

文章来源: lansonli.blog.csdn.net,作者:Lansonli,版权归原作者所有,如需转载,请联系作者。

原文链接:lansonli.blog.csdn.net/article/details/116808054

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。