类、特质、方法和函数都可以有类型参数
将类型参数放置在名称之后,以方括号括起来
参数类型(不能什么都传,要加限定)
对于参数类型的 class 实际类型会在new对象时推断
例如:class Pair[T, S](val first: T, val second: S)
val p = new Pair(42, "String")
参数类型界定
1、<: 和 >: (我定义为 “纯子类界定”)
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什么都可以传,现在就不行了。
你也可以为类型指定一个下界(也就是要求是子类)。
举例,把第一个组件替换为子类。
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[Person],我们想用Student 替换第一个类型,实际上这样是不可行的,因为T必须一致。因此,在函数后面定义下界。
class Pair[T](val first: T, val second: T) {
def replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second)
}
2、<% 视图界定 (我定义为:“含隐式条件的子类界定”)
前面有一个带上界的示例:
把 前面的例子 class Pair[T <: Comparable[T]]
如果你new一个Pair(4,2),编译器会报错,Scala的Int类型并没有实现Comparable。
解决办法是使用视图界定:
class Pair[T <% Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
}
<% 意味着 T 可以被隐式转换成Comparable[Int]。
隐式类型转换,Int 转 RichInt ,RichInt实现了Comparable[Int]
3、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
}
4、<% 上Manifest 上下文界定
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
}
类型约束
类型约束提供的是另一个限定类型的方式。总共有三种关系可供使用:
T=:=U 测试T是否等于U
T<:
T<%
要使用这样一个约束,需要添加“隐式类型证明参数”:
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方法的时候才会报错。
型变:协变和逆变
class Personclass Student extends Person
def makeFriends(p: Pair[Person]) //此函数要对Pair[Person]做某种处理,但是要调用Student里的方法
我们知道,因为虽然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]的超类。这种情况下,需要将类型参数声明为逆变的。
协变、逆变,特质的定义会使用到它
trait Function1 [-T1, +R] extends AnyRef