14

当从 Java 代码中使用从 Scala 代码生成的字节码时,我刚刚发现了 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在使用它之前自愿销毁了它,但问题是在我的心智模型中它甚至不应该编译,更不用说运行良好了。

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

知道发生了什么吗?

4

1 回答 1

23

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

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();
}

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

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

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

于 2016-06-11T09:27:38.523 回答