在Scala中使用Java类时“对重载定义的歧义引用”

时间:2018-08-09 20:59:02

标签: java scala reflection scala-generics

我正在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.scalaComponentUser.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

1 个答案:

答案 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>版本,或者
  • 使用备用库,或
  • 实现您自己的库。

更新

再多考虑一下,您就可以轻而易举地实现您想要的目标。显然,这将取决于您要执行的操作以及实际的MapAComponent类的复杂性,但是您可能会发现更改IComponent来实现{{ 1}},同时在其实现中使用AComponent实例。只要BComponent不必源自IComponent(在AComponent中),该方法就可以工作:

BComponent