"ambiguous reference to overloaded definition" 在 Scala 中使用 Java class
"ambiguous reference to overloaded definition" when using Java class in Scala
我在生成 ambiguous reference to overloaded definition
的 Scala 中使用 Java class。这是解释这个问题的代码。
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: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
bComponent.callme(javaMap)
^
one error found
Java classes 代表一个我无法控制的库。我考虑过使用反射,但它并不能完全满足用例。 super[AComponent].callme
也没有解决问题。如何解决这种情况,以便代码编译并在运行时调用 AComponent.callme
?
已编辑
我对这个答案进行了重大编辑,以解决之前的困惑并使其更加正确。
我认为您正在使用的原始库已损坏,并且没有做它看起来正在做的事情。
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)
(callme(inputMap: java.util.Map[_, _]): Unit
在 Scala 中)。
也就是说,IComponent.callme
接受一个JavaMap
,它的键是一个String
,它的值是一个AnyRef
,而 AComponent.callme
接受 Java Map
其键是 任何类型 并且其值为 任何类型。
默认情况下,Java 编译器毫无怨言地接受它。但是,如果使用 -Xlint:all
选项编译,Java 编译器将发出警告:
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>
参数,一个接受 Map
参数)。然而,与此同时,type erasure(在 运行 时删除通用类型信息)意味着这两种方法在 运行 时看起来也相同(以及使用 Java 反射时)。因此,AComponent.callme
覆盖了 IComponent.callme
(从而履行了 IComponent
接口的契约),同时也使任何后续调用 Acomponent.callme
的 Map<String, Object>
实例不明确。
我们可以这样验证:注释掉BComponent
中的callme
定义,修改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
时,它是明确的(IComponent
只有一个名为 callme
) 并执行由 AComponent
.
提供的覆盖版本
但是,如果我们尝试在原始 aComponent
上调用 callme
会发生什么?
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:
javascalainterop/ComponentUser.scala:14:14: ambiguous reference to overloaded definition,
both method callme in class AComponent of type (x: java.util.Map[_, _])Unit
and method callme in trait IComponent of type (x: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
aComponent.callme(javaMap)
^
请注意,我们甚至还没有查看 BComponent
。
据我所知,没有办法解决 Scala 中的这种歧义,因为这两个歧义函数实际上是一样的,并且具有完全相同的功能运行 时的签名(使反射也无用)。 (如有不明之处,欢迎补充!)
因为 Java 比 Scala 更喜欢这种通用的废话,你可能需要编写所有相关代码在 Java.
中使用此库
否则,您唯一的选择似乎是:
- 报告原始库中的错误(
AComponent.callme
应该接受 Map<String, Object>
参数——在 Java 术语中——而不仅仅是 Map
参数), 或
- 实现您自己的正确工作的
AComponent
版本,或者
- 使用替代库,或者
- 实现你自己的库。
更新
再考虑一下,您也许可以通过一些技巧来实现您想要的目标。显然,这将取决于您要尝试做什么,以及实际 IComponent
和 AComponent
类 的复杂性,但您可能会发现更改 BComponent
很有用实现 IComponent
,同时在其实现中使用 AComponent
实例。如果 BComponent
不必派生自 AComponent
(在 BComponent.scala
中),这应该可以工作:
package javascalainterop
import java.util.{Map => JMap}
class BComponent(inputMessage: String) extends IComponent {
// Create an AComponent instance and access it as an IComponent.
private final val aComponent: IComponent = new AComponent(inputMessage)
// Implement overridden callme in terms of AComponent instance.
override def callme(inputMap: JMap[String, AnyRef]): Unit = {
println(s"Called BComponent.callme with $inputMessage")
aComponent.callme(inputMap)
}
}
我在生成 ambiguous reference to overloaded definition
的 Scala 中使用 Java class。这是解释这个问题的代码。
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: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
bComponent.callme(javaMap)
^
one error found
Java classes 代表一个我无法控制的库。我考虑过使用反射,但它并不能完全满足用例。 super[AComponent].callme
也没有解决问题。如何解决这种情况,以便代码编译并在运行时调用 AComponent.callme
?
已编辑
我对这个答案进行了重大编辑,以解决之前的困惑并使其更加正确。
我认为您正在使用的原始库已损坏,并且没有做它看起来正在做的事情。
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)
(callme(inputMap: java.util.Map[_, _]): Unit
在 Scala 中)。
也就是说,IComponent.callme
接受一个JavaMap
,它的键是一个String
,它的值是一个AnyRef
,而 AComponent.callme
接受 Java Map
其键是 任何类型 并且其值为 任何类型。
默认情况下,Java 编译器毫无怨言地接受它。但是,如果使用 -Xlint:all
选项编译,Java 编译器将发出警告:
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>
参数,一个接受 Map
参数)。然而,与此同时,type erasure(在 运行 时删除通用类型信息)意味着这两种方法在 运行 时看起来也相同(以及使用 Java 反射时)。因此,AComponent.callme
覆盖了 IComponent.callme
(从而履行了 IComponent
接口的契约),同时也使任何后续调用 Acomponent.callme
的 Map<String, Object>
实例不明确。
我们可以这样验证:注释掉BComponent
中的callme
定义,修改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
时,它是明确的(IComponent
只有一个名为 callme
) 并执行由 AComponent
.
但是,如果我们尝试在原始 aComponent
上调用 callme
会发生什么?
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:
javascalainterop/ComponentUser.scala:14:14: ambiguous reference to overloaded definition,
both method callme in class AComponent of type (x: java.util.Map[_, _])Unit
and method callme in trait IComponent of type (x: java.util.Map[String,Object])Unit
match argument types (java.util.HashMap[String,AnyRef])
aComponent.callme(javaMap)
^
请注意,我们甚至还没有查看 BComponent
。
据我所知,没有办法解决 Scala 中的这种歧义,因为这两个歧义函数实际上是一样的,并且具有完全相同的功能运行 时的签名(使反射也无用)。 (如有不明之处,欢迎补充!)
因为 Java 比 Scala 更喜欢这种通用的废话,你可能需要编写所有相关代码在 Java.
中使用此库否则,您唯一的选择似乎是:
- 报告原始库中的错误(
AComponent.callme
应该接受Map<String, Object>
参数——在 Java 术语中——而不仅仅是Map
参数), 或 - 实现您自己的正确工作的
AComponent
版本,或者 - 使用替代库,或者
- 实现你自己的库。
更新
再考虑一下,您也许可以通过一些技巧来实现您想要的目标。显然,这将取决于您要尝试做什么,以及实际 IComponent
和 AComponent
类 的复杂性,但您可能会发现更改 BComponent
很有用实现 IComponent
,同时在其实现中使用 AComponent
实例。如果 BComponent
不必派生自 AComponent
(在 BComponent.scala
中),这应该可以工作:
package javascalainterop
import java.util.{Map => JMap}
class BComponent(inputMessage: String) extends IComponent {
// Create an AComponent instance and access it as an IComponent.
private final val aComponent: IComponent = new AComponent(inputMessage)
// Implement overridden callme in terms of AComponent instance.
override def callme(inputMap: JMap[String, AnyRef]): Unit = {
println(s"Called BComponent.callme with $inputMessage")
aComponent.callme(inputMap)
}
}