命名参数的混淆解析

Confusing resolution of a named argument

下面的代码给我一个 Scala 2.11.7 的错误,这让我很困惑:

class A(val a: String, val bVal: Option[String] = None) {
  val b: String = bVal.getOrElse("")

  def copy(a: String = a, b: Option[String] = Some(b)) = new A(a, b)
}

IntelliJ IDE 没有显示任何错误,但在编译时出现错误:

Error:(4, 52) type mismatch;
 found   : Option[String]
 required: String
  def copy(a: String = a, b: Option[String] = Some(b)) = new A(a, b)
                                                   ^

为了比较,这个编译很好:

class A(val a: String, val bVal: Option[String] = None) {
  val b = bVal
  def copy(a: String = a, b: Option[String] = Some(b.getOrElse(""))) = new A(a, b)
}

当我用 Some(this.b) 替换 Some(b) 时,错误消失了,但我仍然很困惑为什么错误首先出现。看起来编译器正在将 Some 中的 b 解析为 copy 的参数,而不是 Ab 成员。如果是这样,第二个版本怎么编译不出错?

您的理解是正确的,当存在与周围类型成员同名的参数时,编译器将优先选择该参数。

在您的非编译版本中,b 的类型为 Option[String]。这意味着 Some(b) 的类型 Option[Option[String]]A 的构造函数的第二个参数的类型不匹配。

在第二次编译版本中,b仍然是Option[String]类型。但是,b.getOrElse("")String 类型。这意味着 Some(b.getOrElse("")) 的类型 Option[String]A.

的第二个构造函数参数有效

这一定是一个错误。默认表达式的范围包括以前的参数列表。

scala> def x = 1
x: Int

scala> def f(x: Int = x) = 2 * x
f: (x: Int)Int

scala> f()
res0: Int = 2

-Xprint:typer表明默认是正确的:

    class A extends scala.AnyRef {
      <paramaccessor> private[this] val a: String = _;
      <stable> <accessor> <paramaccessor> def a: String = A.this.a;
      <paramaccessor> private[this] val bVal: Option[String] = _;
      <stable> <accessor> <paramaccessor> def bVal: Option[String] = A.this.bVal;
      def <init>(a: String, bVal: Option[String] = scala.None): A = {
        A.super.<init>();
        ()
      };
      private[this] val b: String = A.this.bVal.getOrElse[String]("");
      <stable> <accessor> def b: String = A.this.b;
      def copy(a: String = a, b: Option[String] = scala.Some.apply[A](<b: error>)): A = new $iw.this.A(a, b);
      <synthetic> def copy$default: String = A.this.a;
      <synthetic> def copy$default: Option[String] = scala.Some.apply[String](A.this.b)
    };
    <synthetic> object A extends AnyRef {
      def <init>(): A.type = {
        A.super.<init>();
        ()
      };
      <synthetic> def <init>$default: Option[String] = scala.None
    }

是的。

scala> def y = "hi"
y: String

scala> def g(x: String)(y: Option[String] = Some(x)) = y map (_ * 2)
g: (x: String)(y: Option[String])Option[String]

scala> g("bye")()
res0: Option[String] = Some(byebye)

scala> def g(x: String)(y: Option[String] = Some(y)) = y map (_ * 2)
<console>:11: error: type mismatch;
 found   : Option[String]
 required: String
       def g(x: String)(y: Option[String] = Some(y)) = y map (_ * 2)
                                                 ^

编辑:默认方法正确这一事实并没有多大意义,因为有一些与默认方法不太卫生的事实有关的困惑。例如,http://scalapuzzlers.com/#pzzlr-051