使用名为 "clone" 的隐式 class 方法编译错误

compile error with implicit class method named "clone"

我在尝试使用名为 clone.

的方法进行隐式 class 时遇到意外编译错误(在 2.11.8 中)

以下简化用法:

class Foo(val bar: String)

object Foo {
  implicit class Enrich(foo: Foo) {
    def clone(x: Int, y: Int): Int = x + y
  }
}

object Main extends App {
  val foo = new Foo("hello")
  println(foo.clone(1, 2))    // <- does not compile
}

产生了以下错误:

method clone in class Object cannot be accessed in Foo Access to protected method clone not permitted because prefix type Foo does not conform to object Main where the access take place

但是,我可以手动应用隐式 class 并成功编译:

println(Foo.Enrich(foo).clone(1, 2))    // <- OK

如果我将方法重命名为其他名称(例如 clone2),代码将按预期进行编译。

我假设这与 java.lang.Cloneable 周围的魔术有某种关系,但该方法不需要参数。

那么这是怎么回事?

这与以下事实有关 Object(或 Scala 中的 AnyRef)拥有一个受保护的方法 clone(),该方法优先于 Foo 的重载解析。

SI-6760 部分讨论了这个问题,虽然 clone 有相同的签名,但在这种情况下它是不同的。

这感觉像是一个错误(现在以 SI-10206 打开)。当我们使用 -Ytyper-debug 扩展 typer 树时,您可以看到它找到了 def clone(int, int) 的合适候选者,但随后在紧接着的搜索中失败了:

|-- foo.clone(1, 2) : pt=Unit EXPRmode (site: method main in Main)
|    |    |    |    |-- foo.clone BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Main)
|    |    |    |    |    |-- foo EXPRmode-POLYmode-QUALmode (silent: method main in Main)
|    |    |    |    |    |    \-> foo.type (with underlying type my.awesome.pkg.Foo)
|    |    |    |    |    [search #1] start `my.awesome.pkg.Foo`, searching for adaptation to pt=foo.type => ?{def clone: ?} (silent: method main in Main) implicits disabled
|    |    |    |    |    |-- my.awesome.pkg.Foo.Enrich TYPEmode (site: method Enrich in Foo)
|    |    |    |    |    |    \-> my.awesome.pkg.Foo.Enrich
|    |    |    |    |    |-- Foo TYPEmode (site: value foo in Foo)
|    |    |    |    |    |    \-> my.awesome.pkg.Foo
|    |    |    |    |    |-- Int TYPEmode (site: method clone in Enrich)
|    |    |    |    |    |    \-> Int
|    |    |    |    |    |-- Int TYPEmode (site: value x in Enrich)
|    |    |    |    |    |    \-> Int
|    |    |    |    |    |-- Int TYPEmode (site: value y in Enrich)
|    |    |    |    |    |    \-> Int
|    |    |    |    |    [search #1] considering pkg.this.Foo.Enrich
|    |    |    |    |    |-- pkg.this.Foo.Enrich BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Main) implicits disabled
|    |    |    |    |    |    \-> (foo: my.awesome.pkg.Foo)my.awesome.pkg.Foo.Enrich
|    |    |    |    |    [search #1] success inferred value of type foo.type => ?{def clone: ?} is SearchResult(pkg.this.Foo.Enrich, )
|    |    |    |    |    [search #2] start `my.awesome.pkg.Foo`, searching for adaptation to pt=(=> foo.type) => ?{def clone: ?} (silent: method main in Main) implicits disabled
|    |    |    |    |    \-> <error>
Main.scala:6: error: method clone in class Object cannot be accessed in my.awesome.pkg.Foo
 Access to protected method clone not permitted because
 prefix type my.awesome.pkg.Foo does not conform to
 object Main in package pkg where the access take place
    foo.clone(1, 2) // <- does not compile

编辑

这确实在 2.10.6 下编译

似乎是 Scala 用 AnyRef 屏蔽 java.lang.Object 的方式的错误。我很想说 Scala 的 protected 和 Java 之间的区别可能是罪魁祸首(Java 更明显),但这个例子工作得很好:

public abstract class Bar {
    protected Object method() {
         return null;
    }
}

case class Foo(bar: String) extends Bar

object Example {
    implicit class Enrich(foo: Foo) {
        def method(x: Int, y: Int): Int = x + y
    }

    Foo("abc").method(1, 3)
}

您可以通过覆盖 clone() 使其成为 public 来解决此问题,假设您真的不需要它。

class Foo(val bar: String) {
    override def clone() = this
}

implicit class Enrich(foo: Foo) {
    def clone(x: Int, y: Int): Int = x + y
}

scala> new Foo("abc").clone(1, 2)
res0: Int = 3