2010-12-01 23 views
23

Tengo una pregunta sobre genéricos de Java que esperaba que alguien pudiera responder. Considere el siguiente código:Implementación de interfaz genérica en Java

public interface Event{} 
public class AddressChanged implements Event{} 
public class AddressDiscarded implements Event{} 

public interface Handles<T extends Event>{ 
    public void handle(T event); 
} 

que desea implementar esta interfaz Maneja así:

public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{ 
    public void handle(AddressChanged e){} 
    public void handle(AddressDiscarded e){} 
} 

Pero Java no permite la implementación de manijas dos veces utilizando el genérico. Pude lograr esto con C#, pero no puedo encontrar una solución alternativa en Java sin usar Reflection o instanceof y casting.

¿Existe alguna manera en Java de implementar la interfaz de Handles utilizando ambas interfaces genéricas? ¿O tal vez otra forma de escribir la interfaz de Handles para que el resultado final pueda lograrse?

Respuesta

9

No se puede hacer eso en Java. Solo puede implementar una realización concreta de la misma interfaz genérica. Me gustaría hacer esto en su lugar:

public class AddressHandler implements Handles<Event>{ 
    public void handle(Event e){ 
     if(e instanceof AddressDiscarded){ 
     handleDiscarded(e); 
     } else if(e instanceof AddressChanged){ 
     handleChanged(e); 
     } 
    } 
    public void handleDiscarded(AddressDiscarded e){} 
    public void handleChanged(AddressChanged e){} 
} 
+4

Esto es discutible ... Esta "cascada instanceof" es un procedimiento muy paragidm. Por supuesto que funciona, pero en mi humilde opinión no es muy buen estilo. Pero por lo tanto, tener diferentes nombres de métodos puede no ser perfecto tampoco, es probablemente una cuestión de gusto. –

+1

Esto es lo que haría. No es necesario :) –

+0

-1: puede implementar múltiples interfaces en Java. –

1

yo sepa no se puede hacer eso, porque al compilar el código fuente en Java éstos tanto se reducen a handle(Event), por lo que el método ambiguo.

La información genérica no está disponible durante el tiempo de ejecución en Java, a diferencia de C#. Es por eso que funciona como lo describes.

Deberá cambiar los nombres de los métodos para que sean únicos, como handleAddressChanged y handleAddressDiscarded.

Este es de hecho uno de los puntos débiles de los genéricos de Java.

+0

Es realmente un problema de tiempo de compilación. No necesita saber acerca de las propiedades de los objetos en tiempo de ejecución (¡el argumento podría ser 'nulo'!) Para distinguir los métodos. –

2

No, porque diferentes tipos genéricos "concretos" en Java compilan a del mismo tipo. La interfaz real de su objeto implementará es:

public interface Handles { 
    public void handle(Event event); 
} 

Y, obviamente, no se puede tener dos métodos diferentes con una firma idéntica ...

+0

En realidad, el borrado será 'public void handle (Evento evento);' –

+0

@ S.P.Floyd: D'oh! Fijo. – cdhowie

+0

Como tengo en mi respuesta :-) –

17

Va después @Amir Raminfar, se pueden utilizar visitor patrón

interface Event{ 
void accept(Visitor v); 
} 
interface Visitor { 
void visitAddressChanged(AddressChanged a); 
void visitAddressDiscarded(AddressDiscarded a); 
} 

class AddressChanged implements Event{ 
@Override 
public void accept(Visitor v) { 
    v.visitAddressChanged(this); 
} 
} 

class AddressDiscarded implements Event{ 
@Override 
public void accept(Visitor v) { 
    v.visitAddressDiscarded(this); 
} 
} 

class AddressHandler implements Visitor { 
    void handle(Event e){ 
     e.accept(this); 
    } 
    public void visitAddressChanged(AddressChanged e){} 
    public void visitAddressDiscarded(AddressDiscarded e){} 
} 
+0

+1, pero en el patrón, el evento "acepta" al visitante y el visitante "visita" el evento :) –

+0

@Sylvain M, Gracias. Fijo. –

+0

Evento e no tiene visita() – robev

0

Desafortunadamente no. La solución habitual (grasa, fea, rápida) es crear una interfaz Handles (es decir, HandlesAddressChange, HandlesAddressDiscarded) y dar a cada uno de ellos un método diferente (handleAddressChange(...), handleAddressDiscarded()).

De esta manera, el tiempo de ejecución de Java puede diferenciarlos.

O puede usar clases anónimas.

0

No está permitido porque Java borra las firmas genéricas durante la compilación. El método de interfaz tendrá la firma

public void handle(Object event); 

Tiene dos opciones.O bien implementar controladores separados para diferentes eventos:

public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ } 
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ } 

o implementar un controlador para todos, pero comprobar el tipo de evento entrante:

public void handle(Event e){ 
    if (e instanceof AddressChanged) { 
    handleAdressChanged(e); 
    } 
    else if (e instanceof AddressDiscareded) { 
    handleAdressDiscarded(e); 
    } 
} 
0

Una implementación de este tipo no funcionará debido a las limitaciones de la especificación java. Pero si no tiene miedo de usar AOP o algún tipo de Contenedor de IOC, puede usar anotaciones para eso. Tus Aspectos o el contenedor podrían administrar la infraestructura de mensajería y llamar a los métodos que anotas.

Primero debe crear las anotaciones.

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface EventConsumer {} 

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Handles{} 

El puede anotar su clase así:

@EventConsumer 
public class AddressHandler{ 
    @Handles 
    public void handle(AddressChanged e){} 
    @Handles 
    public void handle(AddressDiscarded e){} 
} 
Cuestiones relacionadas