一、泛型
Scala 和 Java 一样,类、特质、方法都支持泛型。泛型就是定义类、方法时不用指定具体数据类型,在使用时才传入具体适用类型,这样的好处就是支持多类型的复用。泛型的常见用法有以下几种:
- 泛型方法
- 泛型类
- 泛型特质
1.1 泛型方法
泛型方法指把泛型定义到方法声明上,即该方法的参数类型由泛型来决定,在调用该方法时再传入明确的数据类型。
语法格式:
Scala
def 方法名[泛型名称](..) = {
//...
}
例如:
Scala
object Test {
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.2 泛型类
语法格式:
Scala
class 类[T](val 变量名: T)
例如:
- 实现一个 Pari 泛型样例类;
- Pari 类包含两个字段,而且两个字段的类型不固定;
- 创建不同类型泛型类对象;
Scala
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.3 泛型特质
泛型特质指把泛型定义到特质的声明上,即该特质中的成员的参数类型是由泛型来决定的。在定义泛型特质的子类或子单例对象时,明确具体的数据类型。
语法格式:
Scala
trait 特质A[T] {
//特质中的成员
}
class 类B extends 特质A[指定具体的数据类型] {
//类中的成员
}
例如:
- 定义一个泛型特质 Logger,该特质有一个变量 a 和 show() 方法;
- 定义单例对象,继承 Logger 特质;
- 打印单例对象中的成员;
Scala
object Test {
trait Logger[T] {
val a:T
def show(b:T)
}
object ConsoleLogger extends Logger[String]{
override val a:String = "lee"
override def show(b:String):Unit = println(b)
}
def main(args:Array[String]):Unit = {
println(ConsoleLogger.a)
COnsoleLogger.show("abc")
}
}
//lee
//abc
二、泛型的上下界
在定义方法 / 类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时就需要使用到上下界。
2.1 上界
使用 <: 类型名表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承。
Scala
[T <: 类型]
例子:
- 定义一个 Person 类;
- 定义一个 Student 类,继承 Person 类;
- 定义一个 demo 泛型方法,该方法接收一个 Array 参数;
- 限定 demo 方法的 Array 元素类型只能是 Person 或者 Person 的子类;
- 测试调用 demo,传入不同元素类型的 Array;
Scala
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"))
}
2.2 下界
上界是要求必须是某个类的子类,而下界则是必须是某个类的父类(或本身)。
Scala
[T >: 类型]
注意:如果类既有上界又有下界。下界写在前面,上界写在后面,如下:
Scala
def demo[T >: Policeman <: Person ](array:Array[T]) = println(array)
例如:
- 定义一个 Person 类;
- 定义一个Policeman类,继承Person类
- 定义一个Superman类,继承Policeman类
- 定义一个demo泛型方法,该方法接收一个Array参数,
- 限定demo方法的Array元素类型只能是Person、Policeman
- 测试调用demo,传入不同元素类型的Array
Scala
package com.robot.scalademo
class Person
class Policeman extends Person
class Superman extends Policeman
object Demo {
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 Demo {
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的
* */
三、非变、协变、逆变
- 非变:A 类和 B 类是父子类关系,但是 Pair[A] 和 Pair[B] 之间没有任何关系;
- 协变:A 类和 B 类是父子类关系,Pair[A] 和 Pair[B] 之间也有父子类关系;
- 逆变:A 类和 B 类是父子类关系,但是 Pair[A] 和 Pair[B] 之间是子父类关系;
如下图:
3.1 非变
语法格式:
Scala
class Pair[T]{}
- 默认泛型类都是非变的;
- 类型 B 是 A 的子类型,Pair[A] 和 Pair[B] 没有任何从属关系;
- Java 中是一样的;
3.2 协变
语法格式:
Scala
class Pair[+T]
- 类型 B 是 A 的子类型,Pair[B] 可以认为是 Pair[A] 的子类型;
- 参数化类型的方向和类型的方向是一致的;
3.3 逆变
语法格式:
Scala
class Pair[-T]
- 类型 B 是 A 的子类型,Pair[A] 反过来可以认为是 Pair[B] 的子类型;
- 参数化类型的方向和类型的方向是相反的;
3.4 代码示例
- 定义一个 Super 类、以及一个 Sub 类继承自 Super 类;
- 使用协变、逆变、非变分别定义三个泛型类;
- 分别创建泛型类来演示协变、逆变、非变;
Scala
object Demo {
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]的子类型。参数化类型的方向和类型的方向是相反的
}
}