类、特质、方法和函数均可以有类型参数。数组
将类型参数放置在名称以后,以方括号括起来。函数
类型界定的语法为T<: UpperBound
,T>: LowerBound
,T<% ViewBound
,T: ContextBound
。测试
你能够用类型约束来约束一个方法,好比(implicit ev:T<:<UpperBound)
。code
用+T
(协变)来表示某个泛型类的子类型关系和参数T
方向一致,或用-T
(逆变)来表示方向相反。对象
协变适用于表示输出的类型参数,好比不可变集合中的元素。ci
逆变适用于表示输入的类型参数,好比函数参数。get
和Java
和C++
一致,类和特质能够带类型参数。在Scala
中,咱们用方括号来定义类型参数。编译器
class Pair[T, S](val first: T, val second: S)
Scala
会从构造参数中推断出实际类型:虚拟机
val p = new Pair(42, "String")
你也能够本身指定类型:it
val p = new Pair[Any, Any](42, "String")
函数和方法也能够带有类型参数:
def getMiddle[T](a: Array[T]) = a(a.length / 2)
Scala
会从调用该方法使用的实际类型来推断出类型:
val middle = getMiddle(Array("a", "b", "c", "d", "e")) println(middle) //c
有时候你须要对类型变量进行限制。考虑这样一个Pair
类型,它要求它的两个组件类型相同:
class Pair[T](val first: T, val second: T)
如今但愿添加一个方法,产出较小的那个值:
class Pair[T](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second }
这是错的,由于咱们并不知道first
是否有compareTo
方法,要解决这个问题,咱们能够添加一个上界T<:Comparable[T]
。
class Pair[T <: Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second }
注意,这至关因而对类型T加了一条限制:T
必须是Comparable[T]
的子类型。原来给T
指定什么类型均可以,如今就不行了。
你也能够为类型指定一个下界。举例来讲,假定咱们想要定义一个方法,用另外一个值替换对偶的第一个组件。咱们的对偶是不可变的,所以咱们须要返回一个新的对偶。
class Person class Student extends Person class Pair[T](val first: T, val second: T) { def replaceFirst(newFirst: T) = new Pair[T](newFirst, second) }
假定咱们有一个Pair[Student]
,咱们应该容许用一个Person
来替换第一个组件,实际上这样是不可行的,所以,经过在函数后面定义下界来实现。
class Pair[T](val first: T, val second: T) { def replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second) }
一般而言,替换进来的类型必须是原来类型的超类型。为了清晰,我给返回的对偶也写了类型参数,实际上不须要。
在前一节,有一个带上界的示例:
class Pair[T <: Comparable[T]]
若是你试着new
一个Pair(4,2)
,编译器会抱怨说Int
不是Comparable
的子类型,Scala
的Int
类型并无实现Comparable
。不过,RichInt
实现了Comparable[Int]
,同时还有一个从Int
到RichInt
的隐式转换。
解决办法是使用视图界定:
class Pair[T <% Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second }
<%
关系意味着T
能够被隐式转换成Comparable[Int]
。
我的理解:不论是类型变量界定仍是视图界定,实际上都是在限制类型参数T
,类型变量界定要求类型参数T
必须是上界的子类或者是下界的父类;视图界定则是要求类型参数T
必须可以隐式转换成“相似上界”的界定,好比上面提到的,Int
隐式转换成RichInt
,RichInt
是Comparable[Int]
的子类。这样看来,类型变量界定对类型参数的限制比视图界定对类型参数的限制是更大了。
视图界定T<%V
要求T
必须可以隐式转换到V
,上下文界定的形式为T:M
,其中M
是另外一个泛型类。它要求必须存在一个类型为M[T]
的“隐式值”。
class Pair[T: Ordering](val first: T, val second: T) { def smaller(implicit ord: Ordering[T]) = if (ord.compare(first, second) < 0) first else second }
Manifest were added specially to handle arrays
要实例化一个泛型的Array[T]
,咱们须要一个Manifest[T]
对象。要想让基本类型的数组可以正常工做的话,这是必须的。举例来讲,若是T
是Int
,你会但愿虚拟机中对应的是一个int[]
数组。在Scala
中,Array
只不过是类库提供的一个类,编译器并不对它作特殊处理。若是你要编写一个泛型函数来构造泛型数组的话,你须要传入这个Manifest
对象来帮忙。因为它是构造器的隐式参数,你能够用上下文界定:
def makePair[T: Manifest](first: T, second: T) = { val r = new Array[T](2) r(0) = first r(1) = second }
若是你调用makePair(4,9)
,编译器将定位到隐式的Manifest[Int]
并实际上调用makePair(4,9)(intManifest)
。这样一来,该方法调用的就是new Array(2)(intManifet)
,返回基本类型的数组int[2]
。
为何搞这么复杂?在虚拟机中,泛型相关的类型信息是被抹掉的。只会有一个makePair
方法,却要处理全部的类型T
。
类型约束提供的是另外一个限定类型的方式。总共有三种关系可供使用:T=:=U
测试T
是否等于U
T<:<U
测试T
是不是U
的子类T<%<U
测试T
时能可以视图隐式转换为U
要使用这样一个约束,须要添加“隐式类型证实参数”:
class Pair[T](val first: T, val second: T)(implicit ev: T <:< Comparable[T])
不过在上面的例子中,使用类型约束并无比类型变量界定class Pair[T<:Comparable[T]]
有更多的优势。不过在某些场景下,类型约束会颇有用。
类型约束让你能够在泛型类中定义只能在特定条件下使用的方法,示例以下:
class Pair[T](val first: T, val second: T) { def smaller(implicit ev: T <:< Comparable[T]) = if (first.compareTo(second) < 0) first else second } val p1 = new Pair("a", "b") //a
你能够构造出Pair[File]
,尽管File
并非带有前后次序的。只有当你调用smaller
方法的时候才会报错。
假定咱们有一个函数对Pair[Person]
作某种处理:
def makeFriends(p: Pair[Person])
若是Student
是Person
的子类,咱们用Pair[Student]
做为参数调用makeFriends
,这是个错误,由于虽然Student
是Person
的子类,可是Pair[Student]
和Pair[Person]
一点关系都没有。若是你想要这样的关系,则必须在定义Pair
类的时候代表这一点:
class Pair[+T](val first: T, val second: T)
+
号意味着若是Student
是Person
的子类,那么Pair[Student]
也是Pair[Person]
的子类。
也能够有另外一个方向的型变。考虑泛型类型Friend[T]
,表示但愿与类型T
的人成为朋友的人:
trait Friend[-T] { def befriend(someone: T) }
如今假定有一个函数:
def makeFriendWith(s: Student, f: Friend[Student]) { f.befriend(s) }
你能用Friend[Person]
做为参数调用它吗?也就是说,若是你有:
class Person extends Friend[Person] class Student extends Person val susan = new Student val fred = new Person
函数调用makeFriendWith(susan,fred)
能成功吗?看上去应该能够,由于fred
想和任何人叫交朋友,他也必定会和susan
交朋友。注意到这个时候,类型变化的方向和子类型方向是相反的。Student
是Person
的子类,可是Friend[Student]
是Friend[Person]
的超类。这种状况下,须要将类型参数声明为逆变的。