2009-05-02 13 views
8

Recientemente descubrí que puede especificar varios tipos en un solo parámetro de tipo vinculado (ver ejemplo). Como cualquier herramienta nueva, he estado tratando de explorar las posibilidades de cómo se puede usar (y mal). Diseñé este ejemplo para ayudar a ilustrar.¿Cómo puedo resolver los métodos ambiguos causados ​​por los tipos de intersección en los genéricos de Java?

En el ejemplo siguiente, el compilador me da un error

despacho (nueva AlphabetSoup());

El método de envío (Demo.Soup) es ambiguo para la demostración de tipo

Puedo entender esto porque tampoco coincide con firma del método. Mi pregunta es ¿cómo podría resolverse esto sin cambiar los métodos? Si quería forzar una llamada a la versión sopa pude abatido en Soup:

envío ((sopa) nueva AlphabetSoup())

Pero estoy seguro de cómo le gustaría forzar una llamada a la otra versión. ¿Es posible?

public class Demo { 

    interface HasA { public char getA(); } 
    interface HasB { public char getB(); } 
    interface HasC { public char getC(); } 

    interface Soup { 
     public void eat(); 
    } 

    class Alphabet implements HasA, HasB, HasC { 
     public char getA() { return 'a'; } 
     public char getB() { return 'b'; } 
     public char getC() { return 'c'; } 
    } 

    class AlphabetSoup implements Soup, HasA, HasB, HasC { 
     public void eat() { System.out.println("Mmm Mmm Good!"); } 
     public char getA() { return 'a'; } 
     public char getB() { return 'b'; } 
     public char getC() { return 'c'; } 
    } 

    public void dispatch(Soup soup) { 
     System.out.println("Eating some soup..."); 
     soup.eat(); 
    } 

    public <T extends HasA & HasB & HasC> void dispatch(T letters) { 
     System.out.println("Reciting ABCs..."); 
     System.out.println(letters.getA()); 
     System.out.println(letters.getB()); 
     System.out.println(letters.getC()); 
    } 

    public void test() { 
     dispatch(new Alphabet()); 
     dispatch(new AlphabetSoup()); 
    } 


    public static void main(String[] args) { 
     new Demo().test(); 
    } 
} 

- Editar: acaba de aprender que "varios parámetros de tipo acotadas se denominan oficialmente 'los tipos de intersección'

+0

Creo que la única manera posible de llamar al otro método de envío es mediante el uso de la reflexión. –

Respuesta

1

Tenga en cuenta que usted no tiene un verdadero problema, ya que los métodos que le interesan vocación ya se llama debido a la unión dinámica

Correr dispatch((Soup) new AlphabetSoup()); rendimientos:.

Reciting ABCs... 
a 
b 
c 
Eating some soup... 
Mmm Mmm Good! 

por lo tanto, la El método AlphabetSoup ya se llama debido al comportamiento polimórfico básico.

6

El compilador tiene razón, y lo salva de un lío.

AlphabetSoup es un subtipo de sopa y también un subtipo de Hasa, hasB, y hasC

Por lo tanto, cabe la cuenta para ambas versiones de Despacho

Desde sopa no es un subtipo de Hasa, HASB , o HasC, tampoco puede decir que una versión es más "específica" que la otra.

Por lo tanto, obtendrá el error correctamente.

El método sobrecargado no debe ser ambiguo. Si tiene un tipo que mezcla ambos tipos y tiene una sobrecarga para cada uno, cambie su jerarquía o elimine una sobrecarga. Es un uso incorrecto de subtipado y sobrecarga.

+0

excelente explicación :) +1 –

10

Tenga en cuenta que el error no está relacionado con los genéricos, se obtiene el mismo resultado si utiliza las interfaces y un tipo es la intersección:

public class AA { 

    interface XX{}; 
    interface YY{}; 

    public void doSomething(XX x){} 
    public void doSomething(YY x){} 

    class XY implements XX,YY{ 

    } 

    public void runner(){ 
     doSomething(new XY()); 
    } 
} 

se obtiene el mismo error en "doSomething", el compilador no puede resolver la ambigüedad ¿Quieres interpretar como XX o como YY? Tienes que especificarlo con un molde. Pero si tiene una jerarquía, como "YY extends XX" y "XY implementa YY", el compilador puede inferir el método correcto para llamar.

1

No es que se debe mantener el dispatch método sobrecargado (I upvoted Uri por esa razón), pero se puede forzar la versión genérica de ser llamado por tratar:

demo.<AlphabetSoup>dispatch(new AlphabetSoup()); 

o llame a la versión sopa con:

demo.dispatch((Soup) new AlphabetSoup()); 

La mejor manera de evitar esto es no tener el método dispatch sobrecargado en primer lugar.

void dispatchSoup(Soup soup); 
<T extends HasA & HasB & HasC> void dispatchLetters(T letters); 
2

Voy a explicar esto con un programa muy simple:

código de abajo Couse ilustrada de El método es ambiguo para el tipo de error del compilador.

public class AmbiguousMethodOwner { 
      void ambiguousMethod(Comparable c){} 
      void ambiguousMethod(Serializable c){} 
      void test() { 
        ambiguousMethod("bar"); 
      } 
    } 

El problema ahora es obvia: desde Cadena implementa tanto comparable y Serializable, el compilador no puede saber qué método va a llamar.

Un molde sencillo va a resolver el problema:

ambiguousMethod ((comparable) "barra");

http://www.javaneverdie.com/java/the-method-is-ambiguous-for-the-type/

En nuestro despacho método del caso está creando un problema. Ver

class AlphabetSoup implements Soup, HasA, HasB, HasC 

y

public void dispatch(Soup soup) 
public <T extends HasA & HasB & HasC> void dispatch(T letters) { 

ahora si llama dispatch(new AlphabetSoup()); compilador sería confundido en cuanto a cuál es la versión de envío debe ser llamado?

Cuestiones relacionadas