Scala的类型约束

在scala中可以通过隐式参数来进行类型约束。

先来看一串代码:

def test[T](i:T)(implicit ev: T <:< java.io.Serializable) {  
    print("OK") 
}

上述代码中的<:<表示T类型属于java.io.Serializable类型的子类。

类似的还有=:=,也就是说:

A =:= B  //表示A类型等同于B类型  
A <:< B  //表示A类型是B类型的子类型  

这个看上去很像操作符的=:= 和 <:<,实际是一个类,它在Predef里定义:

sealed abstract class =:=[From, To] extends (From => To) with Serializable

sealed abstract class <:<[-From, +To] extends (From => To) with Serializable  

回到最初的代码块,隐式参数ev就是T <:< java.io.Serializable类型的,表示只有参数类型T是java.io.Serializable的子类型,才符合类型要求。

Type类型中的=:=和<:<

在Type类型中=:=和<:<属于方法。举例:

scala> typeOf[List[_]] =:= typeOf[List[AnyRef]]  
res4: Boolean = false

scala> typeOf[List[Int]] <:< typeOf[Iterable[Int]]  
res1: Boolean = true  

对于第一行,相当于:

typeOf[List[_]].=:=(typeOf[List[AnyRef]])

<:和<:<的区别

假设有如下代码:

def foo[A, B <: A](a: A, b: B) = (a,b)

scala> foo(1, List(1,2,3))  
res1: (Any, List[Int]) = (1,List(1, 2, 3))  

传入第一个参数是Int类型,第二个参数是List[Int],显然这不符合 B <: A 的约束,编译器在做类型推导的时候,为了满足这个约束,会继续向上寻找父类型来匹配是否满足,于是在第一个参数被推导为Any类型的情况下,List[Int] 符合Any的子类型。

def bar[A,B](a: A, b: B)(implicit ev: B <:< A) = (a,b)

scala> bar(1,List(1,2,3))  
<console>:9: error: Cannot prove that List[Int] <:< Int.  

通过隐式参数ev来证明类型时,编译器不会向上寻找父类,而是直接报错。

由此可见,在用 <: 声明类型约束的时候,不如用<:<更严格。

参考:

http://hongjiang.info/scala-type-contraints-and-specialized-methods/