一.本章要点
- 类、特质、方法和函数都可以有类型参数
- 将类型参数放置在名称之后,以方括号括起来
- 类型界定的语法为T<:UpperBound、T>:LowerBound、T<%ViewBound、T:ContextBound
- 可以用类型约束来约束一个方法,比如(implicit ev:T<:<UpperBound)
- 用+T(协变来表示某个泛型的子类型关系和参数T方向一致,或用-T(逆变)来表示方向相反
- 协变适用于表示输出的类型参数,比如不可表集合中的元素
- 逆变适用于表示输入的类型参数,比如函数参数
二.泛型类
和Java/C++一致,类和特质都可以带类型参数。在类的定义中,可以用类型参数来定义变量、方法参数、以及返回值类型。例:
class Pair[T,S]{val first:T,val secend:S}
三.泛型函数
函数和方法也可以带类型参数。例:
def getMiddle[T](a:Array[T])=a(a.length/2)
四.类型变量界定
对类型变量进行限制,例:
//错误 class Pair[T](val first:T,val secend:T) //不知道first是否有compareTo方法 {def smaller=if(first.compareTo(secend)<0) first else secend } //解决方法(比较简单,也可以指定一个下界) class Pair[T<:Comparable[T]](val first:T,val secend:T){ def smaller=if(first.compareTo(secend)<0) first else secend }
五.视图界定
”视图界定“,T<%V要求必须存在一个从T到V的隐式转换,如class Pair[T <% Comparable[T]],<%意味着T可以被隐式转换成Comparable[T]。。。
六.上下文界定
格式为T:M,其中M是一个泛型类,要求必须存在一个类型为M[T]的”隐式值“。
七.Manifest上下文界定
要实例化一个泛型的Array[T],需要一个Mainfest[T]对象(要想让基本类型的数组能工作,这是必须的),例:
def makePair(T:Mainfest)(first:T,secend:T){ val r=new Array[T](2);r(0)=first;r(1)=secend;r }
注:如果T是Int,希望虚拟机中对应的是一个int[]数组,在Scala中,Array只不过是类库中提供的一个类,编译器并不对它做特殊处理,因此要编写一个泛型函数来构造泛型数组,需要传入Mainfest对象帮忙(因为在虚拟机中,泛型相关的信息是被抹掉的,只会有一个makePair方法,却要处理所有类型T)。
八.多重界定
类型变量可以同时有上界和下界,写法为T >:Lower <:Upper,不能同时有多个上界或多个下界,但能同时实现多个特质。
可以有多个视图特质:T%<Comparable[T]<%String
可以有多个上下文界定:T:Ordering:Mainfest
九.类型约束
提供的另一种限定类型的方式,总共有三种关系:
T=:=U
T<:<U
T<%<U
这些约束将会测试T是否等于U,是否是U的子类型,或是否能被视图(隐式)转换为U,要使用这样的约束,需要添加”隐式类型证明参数“。
类型参数可在泛型类中定义只能在特定条件下使用的方法,例:
class Pair[T](val first:T,val second:T){ def smaller(implicit ev:T<:<Ordered[T])= if(first<second) first else second }
类型约束的另一个用途是改进类型推断,例:
def firstLast[A,C<:Iterable[A](it:C)=(it.head,it.last)] //执行,推断出的类型参数[Nothing,List[Int]]不符合[A,C<:Iterable[A]] firstLast(List(1,2,3))
十.型变
例:
def makeFriends(p:Pair[Person]) //如果用Pair[Student]作为参数调用makeFriends在缺省情况下是错误的,尽管Student是Person的子类,但Pair[Student]和Pair[Person]没有任何关系 //想要这样的关系,必须在Pair类中表明 //+号意味着该类型是T协变的(与T按同样的类型型变),即Student是Person的子类型=》Pait[Student]是Pair[Person]的子类型 class Pair[+T](val first:T,val second:T) //另一个方向的型变 trait Friend[-T]{ def befriend(someone:T)} //可以是逆变的,Student是Person的子类型,Friend[Student]是Friend[Person]的超类型 class Person extends Friends[Person] class Student extends Person val susan=new Student val fred=new Person makeFriendWith(susan,fred) //注:在一个泛型的类型声明里面,可以同时使用这两种型变
十一.协变和逆变点
通常而言,对于某个对象消费的值适用于逆变,而对于它产出的值则适用于协变。
在Scala中数组是不支持型变的,不能将Array[Student]转换为Array[Person],或者反过来会不安全。。。
十二.对象不能泛型
没法给对象添加类型参数,比如可变列表。。。
十三.类型通配符
在Java中,所有泛型类都是不可变的,可以使用通配符改变它们的类型。
可以在Scala中使用通配符。。。