scala泛型
Scala 泛型:类型安全与灵活性的平衡
泛型是 Scala 中实现类型抽象的核心机制,它允许在定义类、特质、方法时使用类型参数,从而编写与具体类型无关的通用代码。Scala 泛型不仅支持类似 Java 的上下界约束,还引入了协变、逆变等独特特性,进一步增强了类型系统的灵活性。本文将全面解析 Scala 泛型的用法与高级特性。
泛型基础:类型参数的定义与使用
Scala 泛型使用方括号 [] 定义类型参数,可用于类、特质、方法等,实现代码的复用与类型安全。
泛型类
在类定义中声明类型参数,使类能处理多种类型的数据:
// 定义泛型类 Box,类型参数为 T
class Box[T](val content: T) {
// 方法返回值使用类型参数 T
def getContent: T = content
}
// 创建不同类型的 Box 实例
val intBox = new Box[Int](10)
val strBox = new Box[String]("hello")
println(intBox.getContent) // 输出:10(类型为 Int)
println(strBox.getContent) // 输出:hello(类型为 String)
泛型方法
在方法中声明类型参数,使方法能独立于类的类型参数处理不同类型:
object GenericUtils {
// 泛型方法:交换数组中两个位置的元素
def swap[T](array: Array[T], i: Int, j: Int): Unit = {
val temp = array(i)
array(i) = array(j)
array(j) = temp
}
}
// 测试泛型方法
val intArray = Array(1, 2, 3)
GenericUtils.swap(intArray, 0, 2)
println(intArray.mkString(",")) // 输出:3,2,1
val strArray = Array("a", "b", "c")
GenericUtils.swap(strArray, 1, 2)
println(strArray.mkString(",")) // 输出:a,c,b
类型边界:限制泛型的范围
Scala 允许通过上界和下界限制类型参数的范围,确保泛型代码只接受特定类型或其子类型 / 父类型。
上界(<:)
上界限制类型参数必须是指定类型的子类型(类似 Java 的 extends)。
// 定义父类 Animal
class Animal(val name: String) {
def eat: Unit = println(s"$name 正在吃东西")
}
// 子类 Dog
class Dog(name: String) extends Animal(name)
// 子类 Cat
class Cat(name: String) extends Animal(name)
// 泛型类 PetShop,类型参数 T 必须是 Animal 的子类型(上界)
class PetShop[T <: Animal](val pets: List[T]) {
// 调用 Animal 的方法(因 T 是 Animal 子类型,确保有 eat 方法)
def feedAll(): Unit = pets.foreach(_.eat)
}
// 创建 PetShop 实例(只能传入 Dog 或 Cat 等 Animal 子类型)
val dogShop = new PetShop[Dog](List(new Dog("Buddy"), new Dog("Max")))
dogShop.feedAll()
// 输出:
// Buddy 正在吃东西
// Max 正在吃东西
说明:T <: Animal 确保 T 是 Animal 的子类型,因此 PetShop 中可安全调用 Animal 的方法(如 eat)。
下界(>:)
下界限制类型参数必须是指定类型的父类型(类似 Java 的 super)。
// 泛型方法:向列表添加元素,元素类型必须是 T 的父类型
def addElement[T](list: List[T], elem: T): List[T] = elem :: list
// 下界示例:将 Dog 转换为 Animal 后添加到 Animal 列表
def addAnimal[T >: Dog](list: List[T], dog: Dog): List[T] = dog :: list
val animalList: List[Animal] = List(new Cat("Mimi"))
val newList = addAnimal(animalList, new Dog("Buddy"))
newList.foreach(animal => println(animal.name))
// 输出:
// Buddy
// Mimi
说明:T >: Dog 确保 T 是 Dog 的父类型(如 Animal),因此 Dog 实例可安全添加到 List[T] 中。
协变、逆变与不变:泛型类型的继承关系
Scala 泛型的独特之处在于支持协变(Covariance) 和逆变(Contravariance),用于描述泛型类型之间的继承关系。这一特性解决了 Java 中 List<String> 不是 List<Object> 子类型的问题。
基本概念
假设有类型 C[T] 和两个类 A、B,其中 A 是 B 的子类型(A <: B):
- 协变(
C[+T]):C[A] <: C[B](C[A]是C[B]的子类型,与A和B的关系一致)。 - 逆变(
C[-T]):C[B] <: C[A](C[B]是C[A]的子类型,与A和B的关系相反)。 - 不变(
C[T]):C[A]和C[B]无继承关系(默认行为)。
协变(+T)
协变适用于生产者类型(只返回 T,不消费 T),如只读集合。
// 协变类:List 是 Scala 中典型的协变类型(定义为 List[+T])
val dogs: List[Dog] = List(new Dog("Buddy"))
val animals: List[Animal] = dogs // 合法:List[Dog] 是 List[Animal] 的子类型(因协变)
// 自定义协变类(生产者)
class ReadOnlyBox[+T](val content: T) {
def get: T = content // 只返回 T(生产 T)
}
val dogBox: ReadOnlyBox[Dog] = new ReadOnlyBox(new Dog("Max"))
val animalBox: ReadOnlyBox[Animal] = dogBox // 合法:协变
逆变(-T)
逆变适用于消费者类型(只接收 T,不返回 T),如函数或处理器。
// 逆变类:定义一个动物处理器,只消费 T(接收 T 作为参数)
class AnimalHandler[-T] {
def handle(animal: T): Unit = println(s"处理动物:${animal.name}")
}
// AnimalHandler[Animal] 可以处理所有动物,包括 Dog
val animalHandler: AnimalHandler[Animal] = new AnimalHandler[Animal]()
val dogHandler: AnimalHandler[Dog] = animalHandler // 合法:逆变(AnimalHandler[Animal] 是 AnimalHandler[Dog] 的子类型)
dogHandler.handle(new Dog("Buddy")) // 输出:处理动物:Buddy
为什么逆变合理?AnimalHandler[Animal] 能处理所有 Animal(包括 Dog),因此它可以安全地作为 AnimalHandler[Dog] 使用(专门处理 Dog)。
不变(默认)
如果泛型类既生产又消费 T,则必须是不变的,否则会导致类型不安全。
// 不变类:既生产又消费 T
class MutableBox[T](var content: T) {
def get: T = content // 生产 T
def set(value: T): Unit = content = value // 消费 T
}
val dogBox: MutableBox[Dog] = new MutableBox(new Dog("Buddy"))
// 编译错误:MutableBox 是不变的,MutableBox[Dog] 不是 MutableBox[Animal] 的子类型
// val animalBox: MutableBox[Animal] = dogBox
为什么禁止协变?
若允许 MutableBox[Dog] 赋值给 MutableBox[Animal],则可能通过 animalBox.set(new Cat("Mimi")) 向 dogBox 中放入 Cat,导致类型混乱。
泛型通配符(_)
与 Java 类似,Scala 可用通配符 _ 表示 “任意类型”,结合上下界使用:
// 通配符上界:接收任意 Animal 子类型的列表
def printAnimals(list: List[_ <: Animal]): Unit = {
list.foreach(animal => println(animal.name))
}
// 通配符下界:接收任意 Dog 父类型的列表
def addDogToList(list: List[_ >: Dog]): List[_ >: Dog] = {
new Dog("Buddy") :: list
}
val dogs = List(new Dog("Max"))
val cats = List(new Cat("Mimi"))
printAnimals(dogs) // 合法:Dog <: Animal
printAnimals(cats) // 合法:Cat <: Animal
val animals = List(new Animal("Generic Animal"))
val newList = addDogToList(animals) // 合法:Animal >: Dog
泛型的应用场景
- 集合框架:Scala 集合(如
List、Set、Map)广泛使用泛型,List[+T]是协变的,Map[K, +V]中值类型是协变的。 - 函数类型:Scala 函数类型
Function1[-T, +R]是逆变的(参数T)和协变的(返回值R),符合 “消费者逆变,生产者协变” 原则。 - 数据容器:自定义缓存、队列等容器时,用泛型确保类型安全(如
Cache[K, V])。 - 适配器模式:通过泛型适配不同类型的输入输出,如
Converter[-From, +To]。