Как создать пустую неизменяемую карту Scala в Java?

Это должно быть очевидным, но я еще не нашел элегантного решения. По разным причинам мне нужно создать неизменяемую карту Scala (scala.collection.immutable.Map от Scala 2.10), но я могу писать только код Java. Как это сделать?

Ответ 1

Дикая догадка - здесь ничего не получается:

scala.collection.immutable.Map$.MODULE$.<…, …>empty()

Ответ 2

Сначала я был озадачен тем, что только следующее выполнение завершится неудачно:

scala.collection.immutable.Map<Integer, String> = scala.collection.immutable.Map$.<Integer, String>empty();

Причина, по которой это кажется странным, заключается в том, что я знаю, что scala создает статические пересылки, которые внутренне разыгрывают MODULE$ и вызывают соответствующий (нестатический) метод. Я сделал несколько тестов, и хотя то, что я нашел, несколько касательно исходного вопроса, оно все еще связано и хорошо известно.

Скажем, мы имеем следующее:

package test
object MyScalaObject {
  def empty[A, B]: Map[A, B] = sys.error("TODO")
}

Используя javap, мы видим, что создается статический форвардер:

public class test.MyScalaObject extends java.lang.Object{
    public static scala.collection.immutable.Map empty();
    public test.MyScalaObject();
}

И действительно, мы можем в java просто сделать следующее:

scala.collection.immutable.Map<Integer, String> myMap = test.MyScalaObject.<Integer, String>empty();

Все хорошо и приятно. Однако, если мы добавим класс или признак MyScalaObject, который также определяет метод с той же сигнатурой, статический переадресатор не генерируется scala (конечно, поскольку JVM не позволяет классу определять как статическую, так и нестационарную метод с одной и той же сигнатурой). Возможно, существуют и другие различные тонкие ситуации, которые предотвращают создание статических форвардеров.

Рассмотрим:

package test
class MyScalaObject {
  def empty[A, B]: Map[A, B] = sys.error("TODO")
}
object MyScalaObject {
  def empty[A, B]: Map[A, B] = sys.error("TODO TOO")
}

javap показывает, что статический экспедитор действительно исчез:

public class test.MyScalaObject extends java.lang.Object{
    public scala.collection.immutable.Map empty();
    public test.MyScalaObject();
}

В этом случае это означает, что мы должны явно разыменовать $MODULE, который указывает на (уникальный) экземпляр объекта singleton:

scala.collection.immutable.Map<Integer, String> myMap = test.MyScalaObject$.MODULE$.<Integer, String>empty();

Как оказалось, и признак scala.collection.immutable.Map, и его спутник-объект определяют безпараметрический метод empty, поэтому мы натыкаемся на эту самую проблему.

Мораль заключается в том, что, хотя статические экспедиторы являются потенциально полезной функцией, она также очень хрупка, потому что сам факт изменения класса (в этом случае добавление метода empty в классе MyScalaObject) может нарушить функцию, даже не касаясь самого его объекта-компаньона. Поэтому, безусловно, неплохо всегда ссылаться на MODULE$ (как в ответе Уилфреда Спрингера), даже если он выглядит менее приятным и даже если присутствует статический экспедитор в настоящее время, чтобы предотвратить потенциальный обрыв при обновлении к более новой версии библиотеки.