简单回顾泛型

java中可使用泛型进行编程,一个简单的泛型例子如下:

List<String>strList=newArrayList<String>();strList.add("one");strList.add("two");strList.add("three");Stringone=strList.get(0);//泛型拿数据不必进行类型转换,不使用泛型的话需要对类型进行转换scala的泛型

scala中的泛型称为类型参数化(type parameterlization)。语法跟java不一样,使用”[]”表示类型。

一个使用类型参数化的函数:

defposition[A](xs:List[A],value:A):Int={xs.indexOf(value)}position(List(1,2,3),1)//0position(List("one","two","three"),"two")//1

稍微复杂点的类型参数化,实现一个map函数,需要一个List和一个函数作为参数:

普通的map方法:

List(1,2,3)map{_*2}//List[Int]=List(2,4,6)List(1,2,3)map{_+"2"}//List[String]=List(12,22,32)

使用泛型实现的map方法:

defmap[A,B](list:List[A],func:A=>B)=list.map(func)map(List(1,2,3),{num:Int=>num+"2"})//List[String]=List(12,22,32)map(List(1,2,3),{num:Int=>num*2})//List[Int]=List(2,4,6)上限和下限

scala跟java一样,也提供了上限(upper bounds)和下限(lower bounds)功能。

上限(upper bounds)

java中上限的使用如下:

<TextendsObject>通配符形式<?extendsObject>

scala写法:

[T<:AnyRef]通配符形式[_<:AnyRef]

上限的一些例子:

publicvoidupperBound(List<?extendsNumber>list){Objectobj=list.get(0);//Number是Object的子类,使用Object可以代替Number。Numbernum=list.get(0);Integeti=list.get(0);//compileerrorlist.add(newInteger(1));//compileerror}

上限做参数,set的话不能确定具体的类型,所以会报编译错误。get的话得到的结果的类型的下限为参数的上限。相当于使用了上限参数的话,该参数就变成了只读参数,类似生产者,只提供数据。

scala版本:

defupperBound[A<:Animal](list:ListBuffer[A]):Unit={list+=newAnimal("123")//compileerrorvalobj:AnyRef=list(0)//okvala:Animal=list(0)//okvala:Cat=list(0)//compileerror}

这里使用ListBuffer作为集合,ListBuffer的+=方法会在列表内部添加数据,不会产生一个新的List。如果使用List的话,:+操作符会在新生成的List中自动得到符合所有元素的类型。

List[Animal](newCat()):+1//List[Any]=List(Cat@3f23a076,1)

生成新的List会自动根据上下文得到新的泛型类型List[AnyRef]。

下限(lower bounds)

<TsuperMyClass>通配符形式<?superMyClass>

scala写法:

[T>:MyClass]通配符形式[_>:MyClass]

下限的一些例子:

publicstaticvoidlowerBound(List<?superNumber>l){l.add(newInteger(1));l.add(newFloat(2));Objectobj=l.get(0);Numbernum=l.get(0);//compileerror}

下限做参数,get方法只能用最宽泛的类型来获取数据,相当于get只提供了数据最小级别的访问权限。类似消费者,主要用来消费数据。

scala版本:

deflowerBound[A>:Animal](list:ListBuffer[A]):Unit={list+=newAnimal()//oklist+=newCat()//okvalobj:Any=list(0)//okvalobj:Animal=list(0)//compileerror}协变和逆变

协变(covariance):对于一个带类型参数的类型,比如List[T],如果对A及其子类型B,满足List[B]也符合List[A]的子类型,那么就称为协变,用加号表示。比如 MyType[+A]

逆变(contravariance):如果List[A]是List[B]的子类型,用减号表示。比如MyType[+B]

如果一个类型支持协变或逆变,则称这个类型为可变的(variance)。否则称为不可变的(invariant)。

在java里,泛型类型都是不可变的,比如List<String>并不是List<Object>的子类。

traitA[T]classC[T]extendsA[T]classParent;classChildextendsParentvalc:C[Parent]=newC[Parent]//okvalc:C[Parent]=newC[Child];//Child<:Parent,butclassCisinvariantintypeT.协变

上面的例子提示已经很明确了。类C是不可变的,改成协变即可。

traitA[+T]classC[+T]extendsA[T]classParent;classChildextendsParentvalc:C[Parent]=newC[Parent]//okvalc:C[Parent]=newC[Child]//ok

scala中List就是一个协变类。

vallist:List[Parent]=List[Child](newChild())逆变

逆变概念与协变相反。

traitA[-T]classC[-T]extendsA[T]classParent;classChildextendsParentvalc:C[Parent]=newC[Parent]//okvalc:C[Child]=newC[Parent]//ok协变逆变注意点

逆变协变并不会被继承,父类声明为逆变或协变,子类如果想要保持,任需要声明:

traitA[+T]classC[T]extendsA[T]//C是不可变的,因为它不是逆变或协变。classD[+T]extendsA[T]//D是可变的,是协变classE[-T]extendsA[T]//E是可变的,是逆变