Scala
提供了另一种类型界定的手段,与类型界定操作符<:, <%
具有微妙的关系。
T <=< U
:T
是否与U
类型相等T <:< U
:T
是否是U
的子类型T <%< U
:T
是否可以隐式地转换为U
类型
类型界定常常用于以下两种场景:
在泛型类中,定义在特定条件下的才能使用的方法
协助类型推演
特定方法
以T <:< U
为例,capital
的类型为List[(String, String)]
,通过调用toMap
方法可将其转换为Map[String,String]
的类型,这样的转换仅当List
的元素类型为Tuple2
才合法。
val capital = List(
"US" -> "Washington",
"France" -> "Paris"
"Japan" -> "Tokyo")
capital.toMap // OK
当List
的元素类型不是Tuple2
时,试图调用toMap
是编译失败的。
val phones = List("+9519728872", "+9599820012")
phones.toMap // error: Cannot prove that String <:< (T, U).
其中,toMap
定义在TraversableOnce
特质中。
trait TraversableOnce[+A] {
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x
b.result
}
}
其中「隐私参数」implicit ev: A <:< (T, U)
的表示中,<::<
其本质是一个泛型类,其定义在scala.Predef
中。更有甚者,A <:< (T, U)
其实是<:<[A, (T, U)]
的中缀表示。其中,<::<
是一个具有两个类型参数的泛型类。
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
sealed abstract class <:<[-From, +To] extends (From => To)
From => To
其实是Function1[From, To]
特质的一个语法糖。也就是说,<:<
其本质是一个一元函数。接下来的一个疑问是:对于implicit ev: A <:< (T, U)
,编译器如何找到对应的「隐式值」呢?
事实上「隐式值」默认由Predef.conforms
的工厂方法提供,它是一个无参的隐式转换函数。
implicit def conforms[A]: A <:< A = new <:<[A,A] {
override def apply(a: A): A = a
}
val capital = List(
"US" -> "Washington",
"France" -> "Paris"
"Japan" -> "Tokyo")
capital.toMap() // OK
对于此例,conforms[(String, String)]
生成<:<[(String, String), (String, String)]
类型的隐式值。
标准库为了改善性能,避免每次调用toMap
时都new
一个<:<[A,A]
类型的实例,引入了享元模式。
private[this] final val singleton_<:< = new <:<[Any,Any] {
def apply(x: Any): Any = x
}
implicit def conforms[A]: A <:< A = {
singleton_<:<.asInstanceOf[A <:< A]
}
<:
与<:<
def foo[A](i:A)(implicit ev : A <:< Serializable) = i
foo(1) // error
foo("hi") // ok
def bar[A <: Serializable](i:A) = i
bar(1) // compile error
bar("hi") // ok
<:
与<:<
之间到底有什么区别呢?<:
是一个类型限定操作符,编译器保证其子类型的约束关系;而<:<
是一个类型,编译器证明其子类型的约束关系。两者在使用场景,类型推演等方面存在微妙的差异。
def foo[A, B <: A](a: A, b: B) = (a,b)
foo(1, List(1,2,3)) // (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)
bar(1, List(1,2,3)) // error: Cannot prove that List[Int] <:< Int.
而<:<
则更加严格,对于本例因为类型推演优先于隐式解析,第一个参数是Int
类型,第二个参数是List[Int]
,编译器试图证明List[Int] <:< Int
而立即失败。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。