1. 泛型
泛型指泛指某种具体的数据类型,在Scala中泛型用[数据类型]
表示。实际开发中,泛型一般是结合数组或集合来使用的,此外,泛型的常见用法还有以下三种:
- 泛型方法
- 泛型类
- 泛型特质
1.1 泛型方法
泛型方法指把泛型定义到方法声明上,即该方法的参数类型是由泛型来决定的。在调用方法时,明确具体的数据类型。
格式:
def 方法名[泛型名称](...) = {
//...
}
示例:
object ClassDemo {
//方式一:不采用泛型的普通方法
def getMiddleElement(arr:Array[Int]) = arr(arr.length / 2)
//方式二:采用自定义的泛型
//T:type单词的缩写,类型(默认)
def getMiddleElement[T](arr:Array[T]) = arr(arr.length / 2)
def main(args:Array[String]):Unit = {
//测试方式一
println(getMiddleElement(1,2,3,4,5))//3
println(getMiddleElement("a","b","c"))//报错!可不可以把Int向上提升为Any呢?可以但消耗资源
//测试方式二
println(getMiddleElement(1,2,3,4,5))//3
println(getMiddleElement("a","b","c"))//b
}
}
1.2 泛型类
泛型类指把泛型定义到类的声明上,即该类中的成员的参数类型是由泛型来决定的。在创建对象时,明确具体的数据类型。
格式:
class 类[T](val 变量名T)
示例:
object ClassDemo {
//1.定义一个Pair泛型类,该类包含两个字段,且两个字段的类型不固定
class Pair[T](var a:T, var b:T)
def main(args:Array[String]):Unit = {
//2.创建不同类型的Pair泛型类对象,并打印
//val p1 = new Pair[Int](10, "abc") //报错
val p1 = new Pair[Int](10, 20)
println(p1.a, p1.b) //(10, 20)
val p2 = new Pair[String]("a", "b")
println(p2.a, p2.b) //(a, b)
}
}
1.3 泛型特质
泛型特质指把泛型定义到特质的声明上,即该特质中的成员的参数类型是由泛型来决定的。在定义泛型特质的子类或子单例对象时,明确具体的数据类型。
格式:
trait 特质A[T] {
//特质中的成员
}
class 类B extends 特质A[指定具体的数据类型] {
//类中的成员
}
示例:
object ClassDemo {
//1.定义泛型特质Logger,该类包含一个变量a和show()方法,它们都是用Logger特质的泛型
trait Logger[T] {
val a:T
def show(b:T)
}
//2.定义单例对象ConsoleLogger,继承Logger特质
object ConsoleLogger extends Logger[String]{
override val a:String = "lee"
override def show(b:String):Unit = println(b)
}
def main(args:Array[String]):Unit = {
//3.打印单例对象ConsoleLogger中的成员
println(ConsoleLogger.a)
COnsoleLogger.show("abc")
}
}
//lee
//abc
2. 上下界
在使用泛型(方法、类、特质)时,如果要限定该泛型必须从哪个类继承、或者必须是哪个类的父类,此时必须要使用泛型的上下界。
2.1 上界
使用T <: 类型名
表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承
格式:
[T <: 类型]
比如:[T <: Person]
意思是泛型T的数据类型必须是Person类型或Person子类型
示例:
object ClassDemo {
//1.定义一个Person类
class Person
//2.定义一个Student类,继承Person类
class Student extends Person
//3.定义一个泛型方法demo(),该方法接收一个Array参数
//def demo[T](arr:Array[T]) = println(arr)
//4.限定demo方法的Array元素类型只能是Person或Person子类
def demo[T <: Person](arr:Array[T]) = println(arr)
def main(args:Array[String]):Unit = {
//5.调用demo()方法,传入不同元素类型的Array
demo(Array(new Person(), new Person()))
demo(Array(new Student(), new Student()))
//demo(Array("a", "b", "c")) //这行会报错
}
}
//lee
2.2 下界
使用T >: 类型名 表示给类型添加一个下界,表示泛型参数必须要是该类本身或该类型的父类型。
格式:
[T >: 类型]
比如:[T >: Person]
意思是泛型T的数据类型必须是Person类型或Person父类型
注意:
如果泛型既有上界又有下界,下界写在前面,上界写在后面,即:[T >: 类型1 <: 类型2]
示例:
object ClassDemo {
//1.定义一个Person类
class Person
//2.定义一个Policeman类,继承Person类
class Policeman extends Person
//3.定义一个Superman类,继承Policeman类
class Superman extends Policeman
//3.定义一个泛型方法demo(),该方法接收一个Array参数
//def demo[T](arr:Array[T]) = println(arr)
//4.限定demo方法的Array元素类型只能是Person或Policeman
def demo[T >: Policeman](arr:Array[T]) = println(arr)
def main(args:Array[String]):Unit = {
//6.调用demo()方法,传入不同元素类型的Array
demo(Array(new Person()))
demo(Array(new Policeman()))
//demo(Array(new Superman())) //报错
//demo(Array("a", "b", "c")) //报错
}
}
3. 协变、逆变、非变
Spark的源代码中大量使用到协变、逆变、非变。
3.1 非变
格式:
class Pair[T]{}
- 默认泛型类是非变的,即:类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
3.2 协变
格式:
class Pair[+T]{}
- 类型B是A的子类型,Pair[B]是Pair[A]的子类型
- 参数化类型的方向和类型的方向是一致的
3.3 逆变
格式:
class Pair[-T]{}
- 类型B是A的子类型,Pair[A]是Pair[B]的子类型
- 参数化类型的方向和类型的方向是相反的
示例:
object ClassDemo {
//1.定义一个Super类和一个Sub子类
class Super
class Sub extends Super
//2.使用协变、逆变、非变分别定义三个泛型类
class Temp1[T]
class Temp2[+T]
class Temp3[-T]
def main(args:Array[String]):Unit = {
//3.分别创建泛型类对象来演示协变、逆变、非变
//测试非变
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[Sub] = new Temp3[Sub]
//val t6:Temp3[Super] = t5 //编译报错
val t7:Temp3[Super] = new Temp3[Super]
val t8:Temp3[Sub] = t7
}
}
4. 案例:列表去重排序
需求:
import scala.io.Source
object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义数据源对象,关联数据源文件
val source = Source.fromFile("./data/1.txt")
//2.从指定的文件中读取所有的数据
val list1 = source.mkString.split("\\s+").toList
println(list1) //List(11,6,5,3,22,9,3,11,5,1,2)
//3.把List[String] -> List[Int]
val list2:List[Int] = list1.map(_.toInt)
//4.把List[Int] -> Set[Int],对列表元素去重
val set:Set[Int] = list2.toSet
//5.Set[Int] -> List[Int],然后升序排列
val list3:List[Int] = set.toList.sorted
println(list3) //List(1,2,3,5,6,9,11,22)
//6.把所有的数据写入到指定的目的地文件中
//6.1创建字符缓冲流,用来写入数据到指定的目的地文件中
val bw = new BufferedWriter(new FileWriter("./data/2.txt"))
//6.2遍历list3,获取每一个数字
for(i <- list3) {
//6.3将获取到的数字转成字符串后,再写入
bw.write(i.toString)
//6.4换行
bw.newLine()
}
//7.释放资源
bw.close()
source.close()
}
}