如何在Java中创建空的不可变Scala映射?

时间:2013-01-16 18:18:56

标签: java scala map immutability

这应该是显而易见的,但我还没有找到一个优雅的解决方案。由于各种原因,我需要创建一个不可变的Scala映射(来自Scala 2.10的scala.collection.immutable.Map),但我只能编写Java代码。我该怎么做?

2 个答案:

答案 0 :(得分:17)

疯狂的猜测 - 这里什么都没有:

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

答案 1 :(得分: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,它指向单例对象的(唯一)实例:

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

事实证明,特征scala.collection.immutable.Map及其伴随对象都定义了无参数empty方法,因此我们偶然发现了这个问题。

道德观点是,虽然静态转发器是一个潜在有用的功能,但它也非常脆弱,因为仅修改类的事实(在这种情况下在类empty中添加MyScalaObject方法)可以打破这个功能,甚至没有碰到它的伴侣本身。因此,总是明确地引用MODULE$(如Wilfred Springer的答案),即使它看起来不那么好,即使静态转发器当前存在,也是一个好主意,以防止潜在的破坏更新到更新版本的库时。