对于类型限制 =:=
和<:<
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
它定义了两个类型参数,所以可以使用中缀写法:From <:< To
2.10之前还有一个 <%<
类似于view bound,表示 A可以当作B,即A隐式转换成B也满足。但在2.10里已经废弃这种写法。
类型限制用在特定方法(specialized methods)的场景,所谓特定,是指方法只针对特定的类型参数才可以运行:
scala> def test[T](i:T)(implicit ev: T <:< java.io.Serializable) { print("OK") }
test: [T](i: T)(implicit ev: <:<[T,java.io.Serializable])Unit
scala> test("hi")
OK
scala> test(2)
<console>:9: error: Cannot prove that Int <:< java.io.Serializable.
上面定义的test方法,在方法的第二个参数使用了一个隐式参数ev,它的类型是:T <:< java.io.Serializable
,表示只有参数类型T
是java.io.Serializable
的子类型,才符合类型要求。
或许你会奇怪上面test方法调用”hi”时,隐式参数ev是从哪儿传入的?当前并没有定义这个隐式参数。这个隐式参数也是由Predef
里的隐式方法产生的
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
当调用test("hi")
,编译器推断出T是String
,在寻找 String <:< java.io.Serializable
类型的隐式参数时,上下文中找不到,于是通过conforms
隐式方法来产生一个,conforms
方法只有一个类型参数,它产生的结果是<:<[String,String]
类型的对象,但因为<:<[-From,+To]
第一个类型参数是逆变的,第二个类型参数是协变的,所以<:<[String,String]
符合<:<[String,java.io.Serializable]
的子类,满足要求。
而调用test(2)
时,因为隐式方法产生的<:<[Int,Int]
不符合<:<[Int,java.io.Serializable]
子类型,抛出了异常。可见这块编译器是利用函数类型的多态机制来实现类型检测的。
另外,对于Type类型,在判断之间的关系时也有类似的写法,不过这里是Type类型的方法:
scala> typeOf[List[_]] =:= typeOf[List[AnyRef]]
res4: Boolean = false
scala> typeOf[List[Int]] <:< typeOf[Iterable[Int]]
res1: Boolean = true
上面的是方法调用:typ1.=:=(typ2)
,虽然效果都是证明类型关系,但不要混淆。
//2013.11.8 补充
昨天泽彬在csug的旺旺群(94329267/csugcsug)里问<:
与<:<
的差异:
object A{
def test[T <: java.io.Serializable](i:T) {}
test(1) // 编译时报错
def test2[T](i:T)(implicit ev: T <:< java.io.Serializable) {}
test2(1) // 同样编译时报错
}
两者的效果似乎一样,应该怎么选择?他在stackoverflow上也问了一下:http://stackoverflow.com/questions/19829770/whats-different-between-and-in-scala
有人给出这样的解释:
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来证明类型时,类型推断过程不会像上面那样再向上寻找可能满足的情况,而直接报错。
确实,在用 <:
声明类型约束的时候,不如用<:<
更严格,除了上面的类型推导,存在隐式转换的情况下:
scala> def foo[B, A<:B] (a:A,b:B) = print("OK")
scala> class A; class B;
scala> implicit def a2b(a:A) = new B
scala> foo(new A, new B) //存在A到B的隐式转换,也可满足
OK
scala> def bar[A,B](a:A,b:B)(implicit ev: A<:<B) = print("OK")
scala> bar(new A, new B) //隐式转换并不管用
<console>:17: error: Cannot prove that A <:< B.