从 Java 可见的 Scala 中的包私有范围

Posted

技术标签:

【中文标题】从 Java 可见的 Scala 中的包私有范围【英文标题】:Package-private scope in Scala visible from Java 【发布时间】:2016-10-12 05:12:24 【问题描述】:

当从 Java 代码中使用从 Scala 代码生成的字节码时,我刚刚发现了 Scala 范围界定的一种非常奇怪的行为。考虑以下使用 Spark(Spark 1.4、Hadoop 2.6)的 sn-p:

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.broadcast.Broadcast;

public class Test 
    public static void main(String[] args) 
        JavaSparkContext sc = 
            new JavaSparkContext(new SparkConf()
                                .setMaster("local[*]")
                                .setAppName("test"));

        Broadcast<List<Integer>> broadcast = sc.broadcast(Arrays.asList(1, 2, 3));

        broadcast.destroy(true);

        // fails with java.io.IOException: org.apache.spark.SparkException: 
        // Attempted to use Broadcast(0) after it was destroyed
        sc.parallelize(Arrays.asList("task1", "task2"), 2)
          .foreach(x -> System.out.println(broadcast.getValue()));
    

此代码失败,这是意料之中的,因为我在使用它之前自愿销毁了 Broadcast,但问题是在我的心智模型中它甚至不应该编译,更不用说运行良好了。

确实,Broadcast.destroy(Boolean) 被声明为private[spark],所以它不应该在我的代码中可见。我会尝试查看Broadcast 的字节码,但这不是我的专长,这就是我更喜欢发布这个问题的原因。另外,抱歉我懒得创建一个不依赖于 Spark 的示例,但至少你明白了。请注意,我可以使用 Spark 的各种包私有方法,而不仅仅是 Broadcast

知道发生了什么吗?

【问题讨论】:

【参考方案1】:

如果我们用一个更简单的例子来重构这个问题:

package yuvie

class X 
  private[yuvie] def destory(d: Boolean) = true

并在 Java 中反编译:

[yuvali@localhost yuvie]$ javap -p X.class 
Compiled from "X.scala"
public class yuvie.X 
  public boolean destory(boolean);
  public yuvie.X();

我们看到 Scala 中的 private[package] 在 Java 中变成了 public。为什么?这是因为 Java 私有包不等同于 Scala 私有包。有一个很好的解释in this post:

重要的区别是 Scala 中的“private [mypackage]”是 不是 Java 包私有,无论它看起来 多么相似。斯卡拉 包是真正分层的,并且“私有 [mypackage]” 授权 访问类和对象直到“mypackage”(包括所有 可能介于两者之间的分层包)。 (我没有 Scala 对此的规范参考和我在这里的低估可能是模糊的,我是 使用[4] 作为参考。)Java 的包不是分层的,并且 package-private 也仅授予对该包中的类的访问权限 作为原始类的子类,Scala 的“私有” [mypackage]' 不允许。

所以,'package [mypackage]' 与 Java 相比,限制越来越少 包私有。由于这两个原因,不能使用 JVM package-private 实现它,并且是允许使用 Scala 的唯一选项 编译器中的暴露是“公共的”。

【讨论】:

感谢您的回答。你不觉得这对 API 编写者来说有点危险吗?他们从不想暴露的功能最终在 Java 中是显而易见的。我想知道他们是否可以使用一些注释技巧来在用户尝试使用本来是私有的成员时生成警告 @Dici 如果您打算与 Java 互操作,那么是的,我绝对认为这是您必须考虑的事情,特别是如果这会暴露您不希望客户调用的内部结构。尽管在这种特殊情况下,您也可以调用公共 Broadcast.destory 方法,相当于在自己的脚上开枪。 是的,我的意思是现在我知道所有声明为包私有的 Spark 内部组件都通过 Java API 公开,我认为可能应该有更多的 Java 包装器来隐藏没有的功能打算公开。我的示例只是为了显示实际调用了该方法。 任何想要访问他们不应该访问的东西的人都可以使用反射。 @Antimony 你是对的,但是使用反射的努力通常会让人们认为它不值得,不像简单地调用一个就在那里的方法。我同意这可能有问题。

以上是关于从 Java 可见的 Scala 中的包私有范围的主要内容,如果未能解决你的问题,请参考以下文章

如何将 scala 私有范围仅限于一个对象?

Typescript等效的包范围?

Scala中的“评估”

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

为啥私有构造函数在案例类中仍然可见?

如何在 Java 中的包私有声明中破坏封装