博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Scala Essentials: 类型约束
阅读量:6715 次
发布时间:2019-06-25

本文共 2339 字,大约阅读时间需要 7 分钟。

Scala提供了另一种类型界定的手段,与类型界定操作符<:, <%具有微妙的关系。

  • T <=< UT是否与U类型相等

  • T <:< UT是否是U的子类型

  • T <%< UT是否可以隐式地转换为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) = ifoo(1)     // errorfoo("hi")  // okdef bar[A <: Serializable](i:A) = ibar(1)     // compile errorbar("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://dhelo.baihongyu.com/

你可能感兴趣的文章
tr的使用详解
查看>>
CentOS 6.4下PXE+Kickstart无人值守安装操作系统
查看>>
2.5 alias命令
查看>>
arp
查看>>
小博浅谈MVC
查看>>
前端技术学习之选择器(四)
查看>>
Ubuntu与windows的远程控制/远程桌面
查看>>
2016年4月4日中项作业
查看>>
ARP欺骗
查看>>
Oracle专题12之游标
查看>>
两句话笔记--架构学习之一:并发基础课程(2)
查看>>
使用andbug的monitor命令
查看>>
CentOS/RedHat上安装man手册
查看>>
我的友情链接
查看>>
log4j配置
查看>>
去掉Intel集成显卡的桌面右键菜单
查看>>
我的友情链接
查看>>
python pip源配置
查看>>
clamav杀毒软件部署笔记
查看>>
小测试
查看>>