Scala:通过包外的结构类型访问包可见方法

Posted

技术标签:

【中文标题】Scala:通过包外的结构类型访问包可见方法【英文标题】:Scala: Accessing package visible methods through structural types outside the package 【发布时间】:2015-09-26 23:56:44 【问题描述】:

这不能按预期工作(因为我试图从外部 Services 调用包私有 run):

object Services 
 class HelloPrinter 
   private[Services] def run = "Hello"
    


val obj = new Services.HelloPrinter

但是,令人惊讶的是,这有效:

val obj: def run: String = new Services.HelloPrinter
obj.run

我会说,它是编译器中的一个错误,因为由于包可见性规则,HelloPrinter 与结构类型不匹配,所以它根本不应该编译!

这是程序编译但抛出运行时异常(java.lang.NoSuchMethodException)的情况:

class HelloPrinter 
  private[HelloPrinter] def run = "Hello"
  

val obj: def run: String = new HelloPrinter
obj.run

这是我遗漏的语言特性或规则,还是 Scala 中的合法错误?

【问题讨论】:

非常有趣。您是否尝试使用scala -feature REPL 运行?它清楚地表明调用了反射调用。我想在反射方面,作用域和不变性都在窗外。 好吧,据我所知,警告更多是为了让您对可能的性能后果保持谨慎。我的理解是,尽管在运行时使用反射来执行实际调用,但结构类型(据说)与 scala 中的其他任何东西一样是静态类型的,因为编译器将检查签名是否严格匹配(因此反射调用只能成功)。当然,除非您对结构类型执行显式向下转换。但是这里没有强制转换,编译器似乎只是忽略了检查方法的可见性。 你说的第二个例子抛出 NoSuchMethodException 不适合我(在 2.10.4 和 2.11.6 REPL 中)。 【参考方案1】:

在 JVM 级别上,范围为周围实例/类型的可见性不存在。在这种情况下,Scala 编译器将生成一个公共方法并在内部处理这种可见性。

如果您使用结构类型,编译器将反射性地访问该类型的成员。它不会检查 Scala 特定的可见性标志,而只会检查 Java 字节码中定义的那些。

您没有提及您使用的是哪个版本的 Scala 编译器,但我认为这是您特定版本中的错误。尝试编译时,我得到与 Jasper-M 相同的结果。原因是编译器生成的方法实际上是以类型名称为前缀的,在这种情况下是HelloPrinter$$run。将执行以下代码:

val x:  def HelloPrinter$$run: String   = new HelloPrinter
x.run

Scala 编译器再次生成一个公共方法并在内部管理可见性。编译器不检查结构类型的 Scala 内部可见性不是一个特性,而是一个错误。

【讨论】:

以上是关于Scala:通过包外的结构类型访问包可见方法的主要内容,如果未能解决你的问题,请参考以下文章

使用子类引用访问包外的受保护成员

如何保护类,使其在包外不可见

从包外的子类访问受保护的变量

public protected default private

java基础--访问修饰符之我见

如何让 .exe 打包的 Ruby Shoes 应用程序引用包外的资源?