4

Cay Horstmann 的《不耐烦的 Scala》一书中有一条关于 apply 方法的注释:

有时, () 符号会与另一个 Scala 特性发生冲突:隐式参数。例如,表达式会"Bonjour".sorted(3) 产生错误,因为可以选择使用排序调用 sorted 方法,但 3 不是有效的排序。

解决方案是分配"Bonjour".sorted给一个变量并对其调用 apply ,例如:

val result = "Bonjour".sorted
result(3)

或显式调用 apply :

"Bonjour".sorted.apply(3)

但是为什么这不起作用并产生编译错误:

("Bonjour".sorted)(3)

sorted 方法返回 a String,它可以隐式转换为 a StringOps,括号用于包装字符串表达式。为什么编译器不接受调用 a 的 apply 方法StringOps

4

1 回答 1

3

您可以使用-Xprint:parser来查看括号是否被提前丢弃:

scala> implicit class x(val s: String) { def scaled(implicit i: Int) = s * i }
defined class x

scala> "hi".scaled(5)
res0: String = hihihihihi

scala> { implicit val n: Int = 5 ; "hi".scaled }
res1: String = hihihihihi

scala> "hi".scaled(5)(3)
res2: Char = i

scala> { implicit val n: Int = 5 ; ("hi".scaled)(3) }
res3: String = hihihi

scala> :se -Xprint:parser

scala> { implicit val n: Int = 5 ; ("hi".scaled)(3) }
[[syntax trees at end of                    parser]] // <console>
package $line8 {
  object $read extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>() = {
        super.<init>();
        ()
      };
      import $line3.$read.$iw.$iw.x;
      object $iw extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        val res4 = {
          implicit val n: Int = 5;
          "hi".scaled(3)
        }
      }
    }
  }
}

res4: String = hihihi

scala> 

额外的括号什么都不做。编译器只看到一个应用程序expr(args)。因为它是一个应用程序,所以您不会获得“隐式应用程序”转换。

在任何情况下,方法的含义scaled取决于预期的类型。

我们期望额外的括号会有所作为的原因是括号会覆盖运算符的优先级。但(x)只是x

可能规范实际上对此很清楚:

e(args)要求e适用于args. 特别是,根据 的参数类型对 args 进行类型检查e

e(args)被认为e.apply(args)e一个值,但是scaled是一个方法。

您希望“隐式应用程序”插入隐式参数,但这仅适用于e尚未应用的情况。或者(e)(args)可以(e(_))(args)认为是 ,即(x => e(x))(arg)

当写成 时e.apply(arg)e不是类似的应用程序e(arg),因此您可以从隐式应用程序等转换中受益。

于 2018-05-09T18:57:59.483 回答