我正在Scala中使用Java类,该类会生成ambiguous reference to overloaded definition
。这是解释此问题的代码。
IComponent.java
package javascalainterop;
import java.util.Map;
public interface IComponent {
public void callme(Map<String, Object> inputMap);
}
AComponent.java
package javascalainterop;
import java.util.Map;
public class AComponent implements IComponent {
String message;
public AComponent(String message) {
this.message = message;
}
@Override
public void callme(Map inputMap) {
System.out.println("Called AComponent.callme with " + message);
}
}
BComponent.scala
package javascalainterop
import java.util.{Map => JMap}
class BComponent(inputMessage: String) extends AComponent(inputMessage) {
override def callme(inputMap: JMap[_, _]) {
println(s"Called BComponent.callme with $inputMessage")
}
}
ComponentUser.scala
package javascalainterop
import java.util.{HashMap => JHashMap}
object ComponentUser extends App {
val bComponent = new BComponent("testmessage")
val javaMap = new JHashMap[String, AnyRef]
bComponent.callme(javaMap)
}
当我尝试编译BComponent.scala
和ComponentUser.scala
时,编译失败,并显示以下消息。
javascalainterop/ComponentUser.scala:8: error: ambiguous reference to overloaded definition,
both method callme in class BComponent of type (inputMap: java.util.Map[_, _])Unit
and method callme in trait IComponent of type (x$1: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
bComponent.callme(javaMap)
^
one error found
Java类代表一个我无法控制的库。我已经考虑过使用反射,但是它并不能完全满足用例的要求。 super[AComponent].callme
也无法解决问题。如何解决这种情况,以便编译代码并在运行时调用AComponent.callme
?
答案 0 :(得分:2)
已编辑
我已经对该答案进行了重大修改,以解决早期的困惑并变得更加正确。
我认为您正在使用的原始库已损坏,并且没有执行它似乎正在执行的操作。
IComponent
声明方法void callme(java.util.Map<String, Object> inputMap)
(相当于 Scala 中的callme(inputMap: java.util.Map[String, AnyRef]: Unit
),而AComponent
声明void callme(java.util.Map inputMap)
( {<1> Scala 中的callme(inputMap: java.util.Map[_, _]): Unit
。
也就是说,IComponent.callme
接受 Java Map
,其密钥是String
,其值是AnyRef
,而{{1 }}接受 Java AComponent.callme
,其键是任何类型,其值是任何类型。
默认情况下, Java 编译器会毫无保留地接受此声明。但是,如果使用Map
选项进行编译,则 Java 编译器将发出警告:
-Xlint:all
但是,在这种特定情况下,存在一个比仅忽略javascalainterop/AComponent.java:12:1: found raw type: java.util.Map
missing type arguments for generic class java.util.Map<K,V>
public void callme(Map inputMap) {
的类型参数更大的问题。
由于Map
方法的编译时签名与AComponent.callme
方法的编译时签名不同,因此现在看来IComponent.callme
提供了两种不同的AComponent
方法( callme
参数,以及接受Map<String, Object>
参数的参数)。但是,同时,类型擦除(在运行时删除通用类型信息)意味着这两种方法在运行时(以及使用 Java 反射时)看起来也相同。 。因此,Map
会覆盖AComponent.callme
(从而履行IComponent.callme
接口的合同),同时还会使随后与IComponent
实例进行的对Acomponent.callme
的任何调用都不明确。>
我们可以如下验证:在Map<String, Object>
中注释掉callme
定义,并按如下所示更改BComponent
文件的内容:
ComponentUser.scala
运行时,程序现在输出:
package javascalainterop
import java.util.{HashMap => JHashMap}
object ComponentUser extends App {
//val bComponent = new BComponent("testmessage")
val javaMap = new JHashMap[String, AnyRef]
//bComponent.callme(javaMap)
// Test what happens when calling callme through IComponent reference.
val aComponent = new AComponent("AComponent")
val iComponent: IComponent = aComponent
iComponent.callme(javaMap)
}
太好了!我们创建了一个Called AComponent.callme with AComponent
实例,将其转换为一个AComponent
引用,当我们调用IComponent
时,它是明确的(callme
只有一个名为{{1 }}),并执行IComponent
提供的覆盖版本。
但是,如果我们尝试在原始callme
上调用AComponent
,会发生什么情况?
callme
哦,哦!这次,我们从 Scala 编译器收到一个错误,该错误在类型参数方面比 Java 更为严格:
aComponent
请注意,我们还没有看过package javascalainterop
import java.util.{HashMap => JHashMap}
object ComponentUser extends App {
//val bComponent = new BComponent("testmessage")
val javaMap = new JHashMap[String, AnyRef]
//bComponent.callme(javaMap)
// Test what happens when calling callme through each reference.
val aComponent = new AComponent("AComponent")
val iComponent: IComponent = aComponent
iComponent.callme(javaMap)
aComponent.callme(javaMap)
}
。
据我所知,没有办法解决 Scala 中的这种歧义,因为这两个模糊函数实际上是相同的,并且在运行时具有完全相同的签名(反射也没用)。 (如果有人知道,请随时添加评论!)
由于 Java 比 Scala 更适合这种通用的废话,因此您可能需要在 Java 。
否则,您似乎唯一的选择是:
javascalainterop/ComponentUser.scala:14:14: ambiguous reference to overloaded definition,
both method callme in class AComponent of type (x$1: java.util.Map[_, _])Unit
and method callme in trait IComponent of type (x$1: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
aComponent.callme(javaMap)
^
应该接受BComponent
参数(以 Java 的方式,而不仅仅是AComponent.callme
参数),或者< / li>
Map<String, Object>
版本,或者更新
再多考虑一下,您就可以轻而易举地实现您想要的目标。显然,这将取决于您要执行的操作以及实际的Map
和AComponent
类的复杂性,但是您可能会发现更改IComponent
来实现{{ 1}},同时在其实现中使用AComponent
实例。只要BComponent
不必源自IComponent
(在AComponent
中),该方法就可以工作:
BComponent