2010-06-24 17 views
26

Supongamos que tengo estas interfaces:parámetro Usando que implementa múltiples interfaces de pre-genéricos

public interface I1 { 
    void foo(); 
} 

public interface I2 { 
    void bar(); 
} 

y las clases:

public class A extends AParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class B extends BParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class C extends CParent implements I1 { 
    // code for foo method here 
} 

Ahora, con los genéricos puedo tener un método como:

public <T extends I1 & I2> void method(T param) { 
    param.foo(); 
    param.bar(); 
} 

y puedo llamarlo con A y B como parámetros, pero no con C (no implementa I2).

¿Hubo alguna forma de lograr este tipo de seguridad de tipo genérico pre (java < 1.5).

Considere que A, B y C tienen diferentes árboles de herencia, y no es realmente una opción hacer algo como AParent y BParent que tienen un padre común.

Yo sabía que se podía hacer:

public void method(I1 param) { 
    param.foo(); 
    ((I2)param).bar(); 
} 

pero entonces también se podría llamar method(new C()) la que no implementa I2, por lo que se meten en problemas.

¿Hay alguna otra forma en que podría haber hecho esto?

P.S. : Realmente no necesito hacer esto, es principalmente por curiosidad lo que pido.

Respuesta

20

Crear una tercera interfaz I3 extiende I1 e I2. Entonces las clases A y B implementan I3, y el método genérico acepta I3.

Esa es quizás la única forma de hacerlo.

+4

en cierto modo me dio cuenta de esto después hice la pregunta, aunque si tiene más de dos interfaces, y usted quiere mezclar y combinar los diferentes métodos, puede convertirse un dolor al crear una interfaz infantil para cada combinación que desee usar. Estaba pensando más en tener un método como 'method (I1 & I2 param)' o alguna otra forma de decir que el parámetro implementa múltiples interfaces sobre la marcha (sin crear una nueva clase/interfaz) –

+0

No creo esto es bueno en el punto de vista del diseño. Por favor mira mi respuesta. – hqt

2

Antes de Java 1.5 no hay ninguna solución para lograr ese tipo de seguridad en tiempo de compilación. Pero hay un alma en el tiempo de ejecución usando "instanceof".

public void method(Object o) { 
    if (!(o instanceof I1)) 
    throw new RuntimeException("o is not instance of I1"); 

    if (!(o instanceof I2)) 
    throw new RuntimeException("o is not instance of I2"); 

    // go ahead ... 
} 
+0

Bueno, si llamas 'method (new C())' para mi ejemplo no genérico en la pregunta, obtendrás 'ClassCastException' al convertir a' I2' para llamar a 'bar()', así que no hay mucha mejoría aquí. –

+0

No, obtendrás una RuntimeException antes, porque la comprobación de "(o instanceof I2)" se evalúa como falsa;) Bueno, supongo que la sugerencia de sri quizás sea mucho mejor :) Porque no tendrás el riesgo de una excepción de tiempo de ejecución como con mi sugerencia porque el compilador fallará antes. – VuuRWerK

2

sri es la mejor respuesta si tenía permiso para cambiar la firma de A y B. Sin embargo, si no tiene permiso, entonces se podría haber hecho:

public void method(I1 param1 , I2 param2) { // unpopular classes that do not implement I3 must use this method 
    param1.foo(); 
    param2.bar(); 
} 
public void method(I3 param){ // I hope everybody implements I3 when appropriate 
    param.foo(); 
    param.bar(); 
} 
public void method(A param){// A is a popular class 
    method(param,param); 
} 
public void method(B param){// B is a popular class 
    method(param,param); 
} 

Por supuesto, ahora solo use genéricos.

+0

Sí, esto funcionaría, pero si I1 es similar a Comparable, I2 algo así como Serializable, que son ampliamente utilizados, tener un método para cada Tipo que implemente ambos (que llama el privado) podría ser un desafío. Cuando pensé en la pregunta, simplemente di A y B como ejemplos, realmente no me importaría lo que son cuando llamo al método (lo único que importaba era que implementaran esas interfaces). Y, de hecho, si 'method()' podría ser un método API, expuesto para que otros lo utilicen, la codificación difícil de las clases que pueden usarlo sería un problema. –

+0

Entonces, una mejor opción sería hacer público el método privado y luego algunos métodos de conveniencia para las clases de mayor interés, que podrían incluir la interfaz I3 de sri de la interfaz. – emory

6

No creo que las respuestas anteriores sean buenas bajo el punto de vista del diseño. Cuando crea una interfaz, quiere asegurarse de que el objeto llamante tenga responsabilidad para algunas acciones definidas en esa interfaz. Entonces, hay dos soluciones que se discuten arriba, y voy a decir por qué esas soluciones no son buenas en el punto de vista del diseño.

1. hacer una interfaz extiende ambas dos interfaces:

public interface IC extends IA,IB { 
    // empty method here 
} 

Por encima de código es no-sentido. Usted define una interfaz separada solo para combinar otras interfaces, y en el interior no hay ningún método nuevo. No agrega ningún valor a IC en lugar de combinar dos interfaces IA y IB. Y en algunas situaciones, de esta manera hará que su código sea "poco divertido" cuando no puede encontrar el nombre adecuado para la tercera interfaz.Esta situación dará lugar a algún nombre de interfaz como IReadableAndWriteable o ISomethingAndSomethingAndAnotherThing

2. Tipo de fundido en el interior método:

public void methodA(IA object) { 
    if (object instance of IB) { 
    ((IB)(object)).someMethod() 
} 

De esta manera no tiene sentido también. ¿Por qué el parámetro de entrada es IA y luego debe ejecutar alguna acción desde la interfaz IB? Bajo el punto de vista del programador, no hay forma de saberlo, excepto de leer su documento. Eso no es bueno para diseñar una función para el uso de otras personas.

solución verdadera:

soluciones arriba hay otro problema en el diseño: Usted fuerza programador de uso de un objeto que tiene la responsabilidad de dos interfaces. ¿Cuál es el problema si el programador no quiere hacer esto? ¿Quieren usar dos clases diferentes de concreto para cada interfaz diferente para una prueba más sencilla, código limpio? No pueden.

Usted debe hacer dos parámetros diferentes en su firma de método:

public void exampleMethod(IA object1, IB object2) { 
    object1.someMethod() 
    object2.someMethod() 
} 

Y para llamar el método anterior, se pone en el interior mismo parámetro (si el uso del programador mismo objeto). También pueden poner diferentes objetos también.

public void caller() { 
    IC object3 = new C(); // C is the class implements both IA and IB 
    exampleMethod(object3, object3); 
    IA objectA = new A(); 
    IB objectB = new B(); 
    exampleMethod(objectA, objectB);  
} 

Esperanza esta ayuda :)

+2

Lamento haber despertado un tema viejo, pero creo que la adición lo vale. Realmente creo que esta respuesta es la mejor.Sin embargo, para el punto 1, si tiene sentido es más bien subjetivo (puede tener sentido como solución temporal, al menos), pero hay un caso realmente objetivo y práctico que muestra que no es un buen diseño: si implementa una clase que amplía las interfaces relevantes pero * no * la interfaz que las combina, luego no puede usarlo en un método que solicita la interfaz de combinación, aunque respeta el contrato inicial. – Matthieu

+1

No puedo editar la respuesta anterior, así que agrego otra: la idea de crear una interfaz combinada no es buena o está incompleta. No debe ser una mera interfaz, sino un contenedor: si obtiene su implementación de una biblioteca externa, pero quiere que se pueda utilizar cualquier implementación de las dos interfaces, por lo que no quiere que la lib lo limite, entonces necesita un envoltorio, o un adaptador si lo prefiere. Esta solución garantiza que en realidad tenga ambas interfaces asociadas a la misma instancia, mientras que la publicación de hqt anterior no permite garantizar dicha restricción, que aún puede valer la pena. – Matthieu

Cuestiones relacionadas