"Ambigua referencia a la definición sobrecargada" cuando se utiliza la clase Java en Scala

doodlegum:

Estoy usando una clase Java en mi Scala que genera ambiguous reference to overloaded definition. Aquí está el código para explicar este problema.

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)
}

Cuando intento compilar BComponent.scalay ComponentUser.scalala compilación falla con el mensaje abajo.

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

Las clases Java representan una biblioteca que no tengo ningún control sobre. He considerado el uso de la reflexión, pero no acaba de servir al caso de uso. super[AComponent].callmeTambién, no resuelve el problema. ¿Cómo puede la situación se resuelva de manera que las compilaciones de código y AComponent.callmese invoca en tiempo de ejecución?

Mike Allen:

EDITADO

He editado esta respuesta significativamente a resolver la confusión anterior y para ser más correcto.

Creo que la biblioteca original que está trabajando con está roto, y no hacer lo que parece estar haciendo.

IComponentdeclara un método void callme(java.util.Map<String, Object> inputMap)(que es equivalente a callme(inputMap: java.util.Map[String, AnyRef]: Uniten Scala ), mientras que AComponentdeclara void callme(java.util.Map inputMap)( callme(inputMap: java.util.Map[_, _]): Uniten Scala ).

Es decir, IComponent.callmeacepta un Java Map cuya clave es una Stringy cuyo valor es una AnyRef, mientras que AComponent.callmeacepta un Java Map cuya clave es cualquier tipo y cuyo valor es cualquier tipo .

Por defecto, el de Java compilador acepta esto sin queja. Sin embargo, si se compila con la -Xlint:allopción, la de Java compilador emitirá la advertencia:

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) {

Sin embargo, hay un problema mucho mayor en el caso concreto que simplemente omitiendo Map's argumentos de tipo.

Debido a que la signatura de tiempo de compilación de los AComponent.callmedifiere del método de la del IComponent.callmemétodo, ahora parece que AComponentproporciona dos diferentes callmemétodos (uno que toma un Map<String, Object>argumento, y uno que toma un Mapargumento). Sin embargo, al mismo tiempo, el tipo de borrado (eliminación de la información de tipo genérico en tiempo de ejecución) significa que los dos métodos también son idénticas en tiempo de ejecución (y cuando se utiliza Java reflexión). Por lo tanto, AComponent.callmelas anulaciones IComponent.callme(cumpliendo así con el IComponentcontrato de interfaz), mientras que también hace las llamadas subsiguientes a Acomponent.callmeuna Map<String, Object>instancia ambigua.

Podemos comprobar esto de la siguiente manera: comentario a cabo la callmedefinición de BComponenty cambiar el contenido del ComponentUser.scalaarchivo de la siguiente manera:

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)
}

Cuando se ejecuta, el programa ahora da salida:

Called AComponent.callme with AComponent

¡Excelente! Hemos creado una AComponentinstancia, lo convertimos en una IComponentreferencia, y cuando llamamos callme, fue inequívoca (un IComponentsólo tiene un método llamado callme) y ejecuta la versión sustituida proporcionada por AComponent.

Sin embargo, ¿qué ocurre si tratamos de llamar callmeen el 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)
}

¡UH oh! Esta vez, nos sale un error de la Scala compilador, que es un poco más rigurosa respecto a los parámetros de tipo que 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)
              ^

Tenga en cuenta que ni siquiera hemos mirado BComponenttodavía.

Por lo que puedo decir, no hay manera de solucionar este ambigüedad en Scala , ya que las dos funciones ambiguas en realidad son una y la misma, y tienen la misma firma exacta en tiempo de ejecución (haciendo inútil la reflexión también). (Si alguien sabe de otro modo, no dude en enviar un comentario!)

Dado que Java es más feliz con este disparate genérico que Scala , puede que tenga que escribir todo el código relevante que utiliza esta biblioteca de Java .

De lo contrario, parece que sus únicas opciones son:

  • Informar de un error en la biblioteca original ( AComponent.callmedebe aceptar un Map<String, Object>argumento en Java términos, no sólo un Mapargumento), o
  • Poner en práctica su propia versión de AComponentque funciona correctamente, o
  • Utilizar una biblioteca alternativa, o
  • Poner en práctica su propia biblioteca.

ACTUALIZAR

Haber pensado en ello un poco más, usted podría ser capaz de lograr lo que busca hacer con un poco de juego de manos. Está claro que dependerá de lo que estamos tratando de hacer, y la complejidad de lo real IComponenty de AComponentlas clases, pero es posible que le resulte útil para cambiar BComponenta aplicar IComponent, durante el uso de un AComponentejemplo en su aplicación. Esto debería funcionar siempre y cuando BComponentno tenga que ser derivado de AComponent(en 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)
  }
}

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=214229&siteId=1
Recomendado
Clasificación