问题描述
编译器确实知道类型参数 T
是方法体内部的 String
,因为以下示例中的广义类型约束 T =:= String
scala> def f[A](a: A)(implicit ev: A =:= String) = a.toupperCase
def f[A](a: A)(implicit ev: A =:= String): String
然而,尽管拥有这些知识,但似乎还没有完全应用。例如考虑我们如何不能reverse
a String
scala> def f[A <: String](a: A) = a.reverse
def f[A <: String](a: A): String
scala> def f[A](a: A)(implicit ev: A <:< String) = a.reverse
^
error: value reverse is not a member of type parameter A
或者尽管编译器知道 String
是 A
,但我们如何无法将 A
值分配给 String
scala> def f[A](a: String)(implicit ev: A =:= String) = { var x: A = a }
^
error: type mismatch;
found : a.type (with underlying type String)
required: A
如何解释这种行为,即为什么广义类型约束在方法体内部不完全有效?
解决方法
在收集埋藏在评论中的 Luis 点的传统中,=:=
和 <:<
的语法糖的美丽可能掩盖了它们没有什么特别之处的事实。它们类似于
scala> def f[A](a: A)(implicit ev: A => String) = a.toUpperCase
def f[A](a: A)(implicit ev: A => String): String
scala> def f[A](a: A)(implicit ev: A => String) = ev.apply(a).reverse
def f[A](a: A)(implicit ev: A => String): String
编译器在必要时利用证据作为隐式转换
def f[A](a: A)(implicit ev: A => String): String =
ev.apply(a).toUpperCase
def f[A](a: A)(implicit ev: A => String): String =
Predef.augmentString(ev.apply(a)).reverse
| |
| 1st conversion
2nd conversion
在 reverse
的情况下,我们不能简单地编写 a.reverse
,因为在调用 reverse
之前需要进行两次链式转换,而 Scala 不能自动执行此操作,因此我们需要明确说明链中的一步。
将 A =:= String
视为 A => String
,我们看到类型参数 A
在方法主体内保持未特化,因此例如 var x: A = a
无法工作,因为 A
不是绑定到任何特殊类型。