Пакет-частный объем в Scala, видимый с Java
Я только узнал о довольно странном поведении Scala, когда байт-код, сгенерированный из кода Scala, используется из кода Java. Рассмотрим следующий фрагмент, используя 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
.
Любая идея о том, что происходит?
Ответы
Ответ 1
Если мы реконструируем эту проблему с помощью более простого примера:
package yuvie
class X {
private[yuvie] def destory(d: Boolean) = true
}
И декомпилируйте это в Java:
[[email protected] 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. В этом сообщении есть приятное объяснение :
Важным отличием является то, что 'private [mypackage]' в Scala является а не Java-пакет-частный, как бы он ни выглядел. Scalaпакеты действительно иерархичны, а "частные [mypackage]" гранты доступ к классам и объектам до "mypackage" (включая все иерархические пакеты, которые могут находиться между ними). (У меня нет Scalaспецификация для этого, и мое занижение здесь может быть туманным, я используя [4] в качестве ссылки.) Пакеты Java не являются иерархическими, и Пакет-частные гранты доступа только к классам в этом пакете, а также как подклассы исходного класса, что-то, что Scala 'private [mypackage] 'не разрешает.
Итак, 'package [mypackage]' является более и менее ограничивающим, чем Java Пакет-частное. По обоим причинам JVM package-private не может использоваться для его реализации и единственный вариант, который позволяет использовать Scalaв компиляторе открыто "public".