2012-05-09 24 views
11

Tengo el siguiente código en una clase abstracta java:Java: tipo de retorno polimórfico en un método abstracto?

protected abstract <E extends HasText & IsWidget> E createNewDisplayWidget(); 

que compila bien. Sin embargo, si la llamo en cualquier lugar, el compilador se queja:

Bound mismatch: The generic method createNewDisplayWidget() of type DemoClass is not applicable for the arguments(). The inferred type HasText is not a valid substitute for the bounded parameter <E extends HasText & IsWidget> 

¿Hay una manera de exigir un método abstracto para devolver algo que se debe implementar múltiples interfaces?

Nota: No, no puedo crear una interfaz especial que implemente las dos que me gustan. GWT tiene widgets como Label que ya implementan dichas interfaces y me gustaría usar dicho widget.

Editar: me ocurrió la idea de hacer esto de aquí (página 22):

http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

+0

¿Has probado con una coma en lugar de una y? – sp00m

+0

una coma significaría que el límite termina en IsWidget. HasText luego se ignora. El compilador lanza una advertencia que dice que el segundo tipo está 'oculto'. – McTrafik

+4

¿Puede mostrarnos uno/algunos de sus sitios de llamadas? –

Respuesta

2

El problema estaba en Eclipse. Actualicé de 3.7 a 3.7.2 y el error del compilador desapareció.

No sé los detalles del efecto que esto tenía. Si alguien tiene una pista, no dude en actualizar mi respuesta.

+0

Sí, eso tiene sentido. Las [notas de la versión de Eclipse 3.7.1] (http://www.eclipse.org/eclipse/development/readme_eclipse_3.7.1.html#DefectsFixed) enumeran las correcciones [Bug 341795] (https://bugs.eclipse.org /bugs/show_bug.cgi?id=341795), que describe la misma situación, con exactamente el mensaje de error que vio. –

2

me han dado una oportunidad sobre la base de su pregunta y yo era capaz de conseguir a través sin un error . Por favor revisa las clases que he creado.

TestClass

public abstract class TestClass { 

    protected abstract <E extends HasText & IsWidget > E createNewDisplayWidget(); 

} 

clase HasText

public class HasText { 

} 

IsWidget

public interface IsWidget { 

} 

DemoClass

public class DemoClass extends HasText implements IsWidget{ 

    } 

TestClass1

public class TestClass1 extends TestClass{ 

    @Override 
    protected DemoClass createNewDisplayWidget() { 
     // TODO Auto-generated method stub 
     DemoClass type = new DemoClass(); 
     return type; 
    } 

    public void checkOut(){ 
     if(createNewDisplayWidget() instanceof HasText || createNewDisplayWidget() instanceof IsWidget){ 
      System.out.println("Yes it works"); 
     } 
     else{ 
      System.out.println("It doesnt"); 
     } 
    } 

    public static void main(String[] args){ 
     TestClass1 check = new TestClass1(); 
     check.checkOut(); 

    } 

} 

cuando corro mi programa principal que siempre sale "Sí funciona". Por favor, avíseme si me falta algo.

+0

Está llamando 'createNewDisplayWidget()' de la misma clase donde le dio un tipo de devolución más concreto. Intenté llamarlo desde 'DemoClass' pero no pude reproducir el error del OP. Tal vez es un problema específico de GWT. – mpartel

+0

Sí. El problema que estaba tratando de evitar no fue crear otro tipo como "DemoClass". En realidad, es la solución que fui, pero no es lo que estaba buscando porque ahora tengo que crear tipos personalizados para cada clase de GWT que quería usar. – McTrafik

0

Lo que se quiere hacer es sólo posible si Java soporta tipo de interceptación directa, entonces puede simplemente

HasText&IsWidget createNewDisplayWidget(); 

cualquier aplicación debe devolver un objeto que es un subtipo del tipo HasText&IsWidget; en otras palabras, el tipo devuelto debe ser un subtipo de HasText y IsWidget

Desafortunadamente, Java no es compatible con eso.

El problema de su intento de solución se puede entender a partir de dos ángulos:

1) limitaciones de una variable de tipo son limitaciones de la persona que llama; el que llama proporciona el argumento de tipo real y debe cumplir las restricciones. Lo que realmente quieres es restricciones para el destinatario.

2) si una variable de tipo solo aparece en el tipo de devolución, pero no en los tipos de parámetros del método, es generalmente un signo de problema.En Java, debido al mal tipo borrado, el cuerpo del método no puede conocer el argumento de tipo en tiempo de ejecución, por lo tanto, no puede devolver un valor del tipo apropiado deseado por la persona que llama, a excepción de algunos valores triviales como null; otro ejemplo clásico del valor de retorno es trivial

java.util.Collections 
    <T> Set<T> emptySet() 
+0

La respuesta no es correcta, y no debes aceptarla. Lo que es correcto es que dicha firma de método * puede * ser un signo de un problema. Pero para una fábrica que crea un cierto conjunto de subtipos, esto puede estar bien (por conveniencia), y funciona en Java (simplemente agregue '@SuppressWarnings (" desmarcado "), si lo desea. Puede obtener ClassCastExceptions en Runtime, y puede que no sea siempre el mejor enfoque, pero ** ¡esto no explica el error del compilador **! –

+0

Para mí 'En Java, debido al borrado de tipos malvados, el cuerpo del método no puede conocer el argumento del tipo de tiempo de ejecución, por lo tanto, no puede devolver un valor del tipo apropiado deseado por el llamador como sentido. Intenté agregar un parámetro del tipo 'E' a la firma y el compilador dejó de quejarse. Por supuesto, el método se vuelve inutilizable. – McTrafik

1

Así que he intentado hacer el código completo para generar el error, pero por alguna razón no dio un error. Se verá en ella un poco:

import java.util.LinkedList; 

public abstract class DemoClass { 

    public static void main(String[] args) { 
     DemoClass dc = new DemoClassImpl(); 
     dc.createNewLivingAndDying().live(); 
     dc.killAll(); 
    } 

    LinkedList<Dies> dying = new LinkedList<Dies>(); 

    public Lives createNewLivingAndDying() { 
     Lives ab = newLivingAndDying(); // This is where I expected an error 
     dying.add((Dies) ab); 
     return ab; 
    } 

    public void killAll() { 
     for (Dies dead : dying) 
      dead.die(); 
    } 

    protected abstract <E extends Lives & Dies> E newLivingAndDying(); 

} 

class DemoClassImpl extends DemoClass { 

    @SuppressWarnings("unchecked") 
    @Override 
    protected <E extends Lives & Dies> E newLivingAndDying() { 
     return (E) new SomePrivateClass(); // This is what I don't understand 
    } 

} 

interface Lives { 
    public void live(); 
}; 

interface Dies { 
    public void die(); 
}; 

class SomePrivateClass implements Lives, Dies { 

    @Override 
    public void die() { 
     System.out.println("Object Dies"); 
    } 

    @Override 
    public void live() { 
     System.out.println("Object Lives"); 
    } 
} 

Este código se compila y funciona muy bien en mi ordenador personal, pero dador el error en mi equipo de trabajo.

Bound mismatch: The generic method newLivingAndDying() of type DemoClass is not applicable for the arguments(). The inferred type Lives is not a valid substitute for the bounded parameter <E extends Lives & Dies> 

En este momento, creo que es un problema de configuración del proyecto, pero no sé lo que es. JRE 1.6 ambos.

+0

En otras palabras , omite el error al usar 'Object' (implícitamente) como argumento de tipo y luego a una interfaz. Dicho de otra manera si solo necesita' Lives' en alguna parte, 'Lives lives = newLivingAndDying()' no podría compilarse, pero 'Vidas vidas = (Vidas) esto. newLivingAndDying() 'compilaría (¡y funcionaría!) –

+0

@ThomasBroyer' newLivingAndDying() 'no se compilará :) la razón por la cual' Object ab = newLivingAndDying(); 'compila es complicado, podría deberse a peculiaridades en inferencia tipográfica . – irreputable

+0

@McTrafik, mediante conversión, una implementación puede devolver cualquier cosa, independientemente del tipo de devolución declarada. está bien, pero pierdes la verificación del tipo de compilador. también puede declarar 'Object newLivingAndDying()'. – irreputable

Cuestiones relacionadas