这应该是显而易见的,但我还没有找到一个优雅的解决方案。由于各种原因,我需要创建一个不可变的 Scala 映射(Scala 2.10 中的 scala.collection.immutable.Map),但我只能编写 Java 代码。我该怎么做呢?
2 回答
疯狂的猜测 - 这里什么都没有:
scala.collection.immutable.Map$.MODULE$.<…, …>empty()
起初我很困惑,只是执行以下操作会失败:
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 的回答)肯定是一个好主意,以防止在更新到较新版本的库时潜在的损坏。