已编辑
我已经对这个答案进行了重大编辑,以解决早期的混淆并且更正确。
我认为您正在使用的原始库已损坏,并且没有做它看起来正在做的事情。
IComponent
声明一个方法void callme(java.util.Map<String, Object> inputMap)
(相当于callme(inputMap: java.util.Map[String, AnyRef]: Unit
在Scala中),而AComponent
声明void callme(java.util.Map inputMap)
(callme(inputMap: java.util.Map[_, _]): Unit
在Scala中)。
也就是说,IComponent.callme
接受键为 a且值为 an的Java ,同时接受键为任意类型且值为任意类型的Java。Map
String
AnyRef
AComponent.callme
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
但是,在这种特定情况下,还有一个比仅仅省略's 类型参数要大得多的问题。
由于该方法的编译时间签名与该AComponent.callme
方法的不同IComponent.callme
,现在它似乎AComponent
提供了两种不同的callme
方法(一种采用Map<String, Object>
参数,另一种采用Map
参数)。然而,同时,类型擦除(在运行时删除泛型类型信息)意味着这两种方法在运行时(以及使用Java反射时)看起来也相同。因此,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
,它是明确的(IComponent
只有一个名为 的方法callme
)并执行由 提供的覆盖版本AComponent
。
但是,如果我们尝试调用callme
original会发生什么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更严格一些:
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
。
据我所知,在Scala中没有办法解决这种歧义,因为这两个模棱两可的函数实际上是相同的,并且在运行时具有完全相同的签名(也使反射无用)。(如果有人知道,请随时添加评论!)
由于Java比Scala更喜欢这种通用的废话,因此您可能需要在Java中编写使用此库的所有相关代码。
否则,看起来您唯一的选择是:
- 报告原始库中的错误(
AComponent.callme
应该接受一个Map<String, Object>
参数——用Java术语来说——不仅仅是一个Map
参数),或者
- 实现您自己的版本
AComponent
可以正常工作,或者
- 使用替代库,或
- 实现你自己的库。
更新
再多考虑一下,您也许可以通过一点技巧来实现您想要做的事情。显然,这将取决于您要做什么,以及实际IComponent
和类的复杂性,但您可能会发现更改为 implement ,同时在其实现中使用实例AComponent
很有用。如果不必从(in ) 派生,这应该可以工作: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)
}
}