13

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

4

2 回答 2

18

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

scala.collection.immutable.Map$.MODULE$.<…, …&gt;empty()
于 2013-01-16T18:32:12.660 回答
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();

事实证明,traitscala.collection.immutable.Map和它的伴生对象都定义了一个无参数的empty方法,所以我们偶然发现了这个问题。

寓意是,虽然静态转发器是一个潜在有用的特性,但它也非常脆弱,因为仅仅修改一个类(在这种情况下,empty在 class 中添加一个方法MyScalaObject)就可能破坏该特性,甚至不会触及它的伴随对象本身。因此,MODULE$即使它看起来不太好并且即使当前存在静态转发器,始终明确引用(如 Wilfred Springer 的回答)肯定是一个好主意,以防止在更新到较新版本的库时潜在的损坏。

于 2013-01-17T12:49:46.327 回答