2010-08-28 24 views
62

Estoy tratando de definir una clase abstracta que implemente Comparable. Cuando defino la clase con la siguiente definición:Clase abstracta de Java Implementar una interfaz con genéricos

public abstract class MyClass implements Comparable <MyClass> 

subclases tienen que implementar compareTo(MyClass object). En cambio, quiero que cada subclase implemente compareTo(SubClass object), aceptando un objeto de su propio tipo. Cuando intento de definir la clase abstracta con algo como:

public abstract class MyClass implements Comparable <? extends MyClass> 

Se queja de que "Un supertipo puede no especificar cualquier comodín."

¿Hay una solución?

Respuesta

40

Es un poco demasiado detallado en mi opinión, pero funciona:

public abstract class MyClass<T extends MyClass<T>> implements Comparable<T> { 

} 

public class SubClass extends MyClass<SubClass> { 

    @Override 
    public int compareTo(SubClass o) { 
     // TODO Auto-generated method stub 
     return 0; 
    } 

} 
+0

Creo que eso es todo. – Pointy

+2

Considere (1) la clase MyImpl1 extiende MyClass {...}; y (2) la clase MyImpl2 extiende MyClass {public int compareTo (MyImpl1 o) {...}}. MyImpl2 no está haciendo lo correcto. – emory

+1

Si suponemos que cada subclase extiende MyClass con su propia clase como parámetro genérico, la solución es correcta. Sin embargo, parece que no hay forma de garantizar esto, como señaló emory. – Cem

1

No estoy seguro de que lo que necesita la captura:

En primer lugar, añadir el compareTo a la clase abstracta ...

public abstract class MyClass implements Comparable <MyClass> { 

@Override 
public int compareTo(MyClass c) { 
... 
}  
} 

A continuación, agregue las implementaciones ...

public class MyClass1 extends MyClass { 
... 
} 

public class MyClass2 extends MyClass { 
... 
} 

Calling comparar llamará al método de tipo súper ...

MyClass1 c1 = new MyClass1(); 
MyClass2 c2 = new MyClass2(); 

c1.compareTo(c2); 
+0

¿No es exactamente así como Cem describió su problema? ¿Cómo implementaría 'compareTo' en' MyClass1' o 'MyClass2' con un tipo de parámetro diferente? – whiskeysierra

+0

Estás en lo correcto ... lee la pregunta al revés. – zevra0

17

Aparte de las dificultades mecánicas que estamos encontrando declarar las firmas, el objetivo no tiene mucho sentido. Está intentando establecer una función de comparación covariante, que rompe la idea de establecer una interfaz que las clases derivadas puedan adaptar.

Si define alguna subclase SubClass tal que sus casos sólo pueden ser comparados con otros SubClass casos, entonces, ¿cómo SubClass satisfacer el contrato definido por MyClass? Recuerde que MyClass dice que él y cualquier tipo derivado de él se pueden comparar con otras instancias de MyClass. Está tratando de hacer que eso no sea cierto para SubClass, lo que significa que SubClass no cumple con el contrato de MyClass: No puede sustituir SubClass por MyClass, porque los requisitos de SubClass son más estrictos.

Este problema se centra en la covarianza y la contravarianza, y cómo permiten que las firmas de funciones cambien a través de la derivación de tipo. Puede relajar un requisito en un tipo de argumento, que acepta un tipo más amplio que las demandas de firma del supertipo, y puede fortalecer un requisito en un tipo de retorno, prometiendo devolver un tipo más estrecho que la firma del supertipo. Cada una de estas libertades aún permite la sustitución perfecta del tipo derivado por el supertipo; un llamante no puede notar la diferencia cuando utiliza el tipo derivado a través de la interfaz del supertipo, pero un llamador que utiliza el tipo derivado puede aprovechar estas libertades en concreto.

Willi's answer enseña algo sobre las declaraciones genéricas, pero le insto a que reconsidere su objetivo antes de aceptar la técnica a expensas de la semántica.

+0

Estoy de acuerdo con esta respuesta.Además, deberíamos codificar las interfaces, no las clases. La clase de implementación real puede ser una clase anónima, una clase local (anidada dentro de un método), privada (anidada dentro de una clase) o un paquete privado y, por lo tanto, no está dentro de nuestro alcance. – emory

+2

Otro problema que veo es almacenar los objetos de subclase en una Colección. Debería ser capaz de almacenarlos con 'List ', en lugar de 'List >'. ¿Y cómo se llamaría cuando obtienes uno de esos objetos y llamas a 'equals (anObject)'? – TheLQ

+0

seh, gracias por su respuesta. En realidad, estoy buscando un contrato que obligue a cada subclase a tener un método compareTo() solo para su propia clase, pero no para ninguna otra subclase. Mi definición genérica inventada podría ser engañosa en este sentido. – Cem

3

ver el propio ejemplo de Java:

public abstract class Enum<E extends Enum<E>> implements Comparable<E> 
    public final int compareTo(E o) 

en el comentario de SEH: por lo general el argumento es correcto. pero los genéricos hacen que las relaciones tipo sean más complicadas. Una subclase puede no ser un subtipo de MiClase en solución de Willi ....

SubClassA es un subtipo de MyClass<SubClassA>, pero no es un subtipo de MyClass<SubClassB>

tipo MyClass<X> define un contrato para compareTo(X) la que todos sus subtipos debe honor. no hay problema allí.

+1

Ese es un buen ejemplo, aunque difiere de la pregunta original de Cem. Ahora, puedo haber estado leyendo su código demasiado literalmente; quizás esto es exactamente lo que estaba * intentando * escribir. En este caso, la faceta 'Comparable' de la interfaz' Enum' es sobre lo que una subclase específica puede hacer con * en sí * (o, más bien, instancias de sí mismo), no con lo que puede hacer con los tipos derivados de 'Enum' en general. Si eso es lo que buscaba Cem, entonces considero que tu respuesta es más apropiada que la mía. – seh

+0

Supongo que está bien siempre y cuando todas las subclases sean un subtipo de MyClass . No estoy seguro, sin embargo, si causa un problema en futuros pasos. Esta es la primera vez que estoy diseñando con genéricos. – Cem

+0

Este es un mal ejemplo. Enum es un caso especial: los tipos enum son proporcionados por el lenguaje y tienen una forma específica (un 'enum A' implementará Enum ') que no es verdadero para las clases definidas por el usuario en general. – newacct

1
public abstract class MyClass<T> implements Comparable<T> { 

} 

public class SubClass extends MyClass<SubClass> { 

    @Override 
    public int compareTo(SubClass o) { 
     // TODO Auto-generated method stub 
     return 0; 
    } 

} 
+0

Esto no limita el parámetro de tipo T a las subclases de MyClass. –

+0

@ StevoSlavić: ¿Entonces? Es perfectamente seguro para los tipos. – newacct

+0

Si entendí correctamente la pregunta/ejemplo original, una de las ideas fue restringir T a las subclases de MyClass, mientras que el compilador no lo permite. –

0

Sé que dijo que desea "compareTo (objeto de subclase), aceptando un objeto de su propio tipo", pero todavía sugiero que declara la clase abstracta como esto:

public abstract class MyClass implements Comparable <Object> 

y hacer una instanceof comprobar cuándo anulando compareTo en MySubClass:

@Override 
public int compareTo(Object o) { 
    if (o instanceof MySubClass)) { 
     ... 
    } 
    else throw new IllegalArgumentException(...) 
} 

de manera similar a 'iguales' o 'clon'

1

Encontrado otra solución:

  1. definir una interfaz en los campos que componen la comaprable (por ejemplo ComparableFoo)
  2. implementar la interfaz en la clase padre
  3. Implementar Comparable en la clase padre.
  4. Escribe tu implementación.

solución debería tener este aspecto:

public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> { 
    public int compareTo(ComparableFoo o) { 
    // your implementation 
    } 
} 

Esta solución implica que más las cosas podrían implementar ComparableFoo - esto es probablemente no es el caso, pero entonces usted es la codificación de una interfaz y la expresión genéricos es simple .

+0

Me gusta esta solución debido a su simplicidad – Pehmolelu

Cuestiones relacionadas