Scala 中的包私有作用域在 Java 中可见

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

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

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

对正在发生的事情有任何想法吗?


答案 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中变成了Java。为什么?这是因为Java私有包并不等同于Scala私有包。在这篇文章中有一个很好的解释:private[package]public

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

因此,'package [mypackage]“比Java包私有的限制更多,也更少。由于这两个原因,JVM包私有不能用于实现它,并且允许Scala在编译器中公开的唯一选项是“公共”。


答案 2

推荐