Scala学习十七——类型参数

一.本章要点

  • 类、特质、方法和函数都可以有类型参数
  • 将类型参数放置在名称之后,以方括号括起来
  • 类型界定的语法为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中使用通配符。。。  

十四.练习

 

 

posted @ 2019-12-03 20:27  biu嘟  阅读(309)  评论(0编辑  收藏  举报