Scala泛型
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是可变的,是逆变
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。