隐含参数
隐含参数列表(implicit p1,...,pn)将参数p1,...,pn标记为隐含的。一个方法或构造器仅能有一个隐含参数列表,且必须是给出的参数列表的最后一个。
具有隐含参数列表的方法可以像正常方法一样应用到参量上。这种情况下implicit标识符没有作用。然而如果该方法没有隐含参数列表中的参量,对应的参量会自动提供。
有两种情况实体参量可以传递给类型为T隐含参数。首先,所有的标识符x可以在方法被调用的地方无需前缀就可以访问到,且该标识符表示一个隐含定义(§7.1)或隐含参数。一个可用的标识符可以是一个本地命名,或封闭模板的一个成员,或者通过import子句使其不用前缀即可访问。其次,在隐含参数的类型T的隐含作用域中的对象的implicit成员也是可用的。
类型T的隐含作用域由与隐含参数的类型相关联的类的所有伴随模块(§5.4)构成。类C与类型T相关联的条件是它是T的某部件的基类(§5.1.2)。类型T的部件指:
如果T是一个复合类型T1 with ... with Tn,则是T1,...,Tn的部件以及T自身的合集。
如果T是一个参数化的类型S[T1,...,Tn],则是S的部件以及T1,...,Tn的合集。
如果T是一个单例类型p.type,则是p的类型的部件
如果T是一个类型投影S#U,则是S的部件和T自身
其他情况下则只是T自身
如果有多个可用的参量与隐含参数的类型匹配,则将使用静态重载解析(§6.25.3)来选择一个最具体的。
示例7.2.1 还是示例7.1.1中的那些类,现在有一个方法用幺半群的add和unit操作来计算一个元素列表的和。
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if(xs.isEmpty) m.unit
else m.add(xs.head, sum(xs.tail))
这里的幺半群被标记为一个隐含参数,且可通过列表的类型来推断。考虑调用sum(List(1,2,3))在上下文中stringMonoid和intMonoid都是可见的。我们知道sum的正式类型参数a需要初始化为Int。与隐含正式参数类型Monoid[Int]匹配的唯一可用对象是intMonoid,所以该对象会作为隐含参数被传递。
这里同样说明了隐含参数在所有类型参量被推断(§6.25.4)之后才被推断。
隐含方法自身同样可以有隐含参数。以下是模块scala.List中的一个例子,这里将列表注入到scala.Ordered类中,列表的元素类型也可以转化为这里的类型。
implicit def list2ordered[A](x: List[A])
(implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] =
...
假设再增加一个方法
implicit def int2ordered(x: Int): Ordered[Int]
该方法将整数注入到Ordered类中。现在我们可以在ordered列表上定义一个sort方法:
def sort[A](xs: List[A])(implicit a2ordered: A => Ordered[A]) = ...
以下我们将方法sort应用到整数列表的列表yss: List[List[Int]]上:
sort(yss)
以上的调用将通过传递两个嵌套的隐含参量完成:
sort(yss)(xs: List[Int] => list2ordered[Int](xs)(int2ordered))
将隐含参量传递给隐含参量将有可能导致死循环。比如,试图定义以下方法,将任何类型都注入到Ordered类中:
implicit def magix[A](x: A)(implicit a2ordered: A =>
Ordered[A]): Ordered[A]): Ordered[A] = a2ordered(x)
现在,如果尝试将sort应用于没有另外注入到Ordered类中的参量arg,则将得到无限扩展:
sort(arg)(x => magic(x)(x => magic(x)(x => ... )))
为了避免这类无限扩展发生,编译器将为当前搜索的隐含参量创建一个“开放隐含类型”堆栈。当一个类型T的隐含参量被搜索时,T的“核心类型”将会被添加到堆栈中。这里T的核心类型是别名已展开,移除了顶级类型标注和修饰,且将顶级存在边界变量用他们的上界替换后的T。在对隐含参数的搜索完成后,不管搜索成功与否,核心类型都会被从堆栈中删除。每当有一个核心类型添加到堆栈中,将会检查该类型没有影响到集合中的任何其他类型。
这里一个核心类型T影响到一个类型U的情况是T等价于U,或T和U的顶级类型构造器有共有元素且T比U更复杂。
类型T的顶级类型构造器集合ttcs(T)与类型的形式有关:
对于类型指示器,
ttcs(p.c) = {c}
对于参数化类型,
ttcs(p.c[targs]) = {c}
对于单例类型,
ttcs(p.type) = ttcss(T),p的类型是T
对于复合类型,
ttcs(T1 with ... with Tn) = ttcs(T1) U ... U ttcs(Tn)
核心类型的复杂度complexity(T)是一个依赖于类型的形式的整数:
对于类型指示器,
complexity(p.c) = 1 + complexity(p)
对于参数化类型,
complexity(p.c[targs]) = 1 + Σcomplexity(targs)
对于表示包p的单例类型,
complexity(p.type) = 0
对于其他单例类型,
complexity(p.type) = 1 + complexity(T),p的类型是T
对于复合类型,
complexity(T1 with ... with Tn) = Σcomplexity(Ti)
示例7.2.2 对于某些类型为List[List[List[Int]]]的列表xs,sort(xs)类型化的隐含参量类型搜索序列是
List[List[Int]] => Ordered[List[List[Int]]],
List[Int] => Ordered[List[Int]]
Int => Ordered[Int]
所有的类型都共享类型构造器scala.Function1,但是每个新类型的复杂度要比之前的类型低。这就是代码的类型检查方式。
示例7.2.3 设ys是某些不能转变为Ordered的类型的List,例如:
val ys = List(new IllegalArgumentException, new ClassCastException, new Error)
假设上下文中有以上定义的magic。则隐含参量类型搜索的序列是
Throwable => Ordered[Throwable],
Throwable => Ordered[Throwable],
...
由于序列中的第二个类型等价于第一个,编译器将产生一个发散隐含扩展错误。
更多精彩内容请关注:http://bbs.superwu.cn
关注超人学院微信二维码:
关注超人学院java免费学习交流群: