Это должно быть очевидным, но я еще не нашел элегантного решения. По разным причинам мне нужно создать неизменяемую карту Scala (scala.collection.immutable.Map от Scala 2.10), но я могу писать только код Java. Как это сделать?
Как создать пустую неизменяемую карту Scala в 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$
(как в ответе Уилфреда Спрингера), даже если он выглядит менее приятным и даже если присутствует статический экспедитор в настоящее время, чтобы предотвратить потенциальный обрыв при обновлении к более новой версии библиотеки.