【问题标题】:compile error with implicit class method named "clone"使用名为“克隆”的隐式类方法编译错误
【发布时间】:2017-02-23 18:26:03
【问题描述】:

我在尝试使用名为 clone 的方法创建隐式类时遇到意外编译错误(在 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
}

产生以下错误:

类 Object 中的方法克隆不能在 Foo 中访问 不允许受保护的方法克隆,因为前缀类型 Foo 不允许 符合访问发生的对象 Main

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

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

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

我认为这在某种程度上与 java.lang.Cloneable 周围的魔法有关,但该方法不期望参数。

那么这里发生了什么?

【问题讨论】:

    标签: scala scala scala-2.11


    【解决方案1】:

    这与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 中,Object 的等价物是 AnyRef
    • @Andrei 谢谢。固定。
    • 所以参数数量/类型错误的受保护方法优先于参数数量/类型正确的隐式方法?
    • 我看到finalize 也会出现这个问题(作为protected Java-inherited 超级方法的另一个例子),但如果我定义自己的protected 方法就不会出现Scala 中的一个超级特征。
    • @BrianKent 我正在深入研究 AST 类型的生成,看看是否能提出更合理的解释。
    【解决方案2】:

    Scala 用AnyRef 掩盖java.lang.Object 的方式似乎是一个错误。我很想说 Scala 和 Java 的 protected 之间的区别可能是罪魁祸首(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() 将其公开来解决此问题,假设您并不真的需要它。

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-31
      • 2014-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多