2009-08-06 24 views
7

Realmente intento gustarme de los genéricos, pero hasta ahora el problema que han causado supera cualquier beneficio. Por favor, muéstrame que estoy equivocado.Casting a Comparable, luego comparando

Entiendo la necesidad de agregar @SuppressWarnings ("desmarcado") al utilizar marcos genéricos libres (Spring, Hibernate). Esto solo realmente reduce el valor de los genéricos, al igual que las clases que requieren pasar al constructor para evitar las trampas de borrado. Sin embargo, la verdadera espina siempre parece estar lanzando. Por lo general, intento por un tiempo obtener la sintaxis correcta, pero luego renuncio a mi intento de pureza, agrego un @SuppressWarnings y sigo con mi vida.

Aquí hay un ejemplo: estoy reflexionando sobre un frijol para buscar diferencias entre dos instancias. Algunas propiedades implementan Comparable de modo que (a.equals (b) == false) pero (a.compareTo (b) == 0) (por ejemplo, BigDecimal, Date). En estos casos, quiero que la propiedad se considere igual.

MyObject original = getOriginal(); 
MyObject updated = getUpdated(); 
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) { 
    // Assume I'm putting in the try/catch block 
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null); 
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null); 

    boolean isPropertySame; 

    if (Comparable.class.isAssignableFrom(pOriginal.getClass())) { 
     // Type safety: The method compareTo(Object) belongs to the raw type Comparable. References to generic type Comparable<T> should be parameterized 
     isPropertySame = Comparable.class.cast(pOriginal).compareTo(Comparable.class.cast(pUpdated)) == 0; 

     // The method compareTo(capture#19-of ?) in the type Comparable<capture#19-of ?> is not applicable for the arguments (capture#21-of ? extends Comparable) 
     Comparable<?> comparable = Comparable.class.cast(pOriginal); 
     isPropertySame = comparable.compareTo(comparable.getClass().getTypeParameters()[0].getGenericDeclaration().cast(pUpdated)) == 0; 

     // Even if I get the generics right, I still get an error if pOriginal is java.sql.Timestamp and pUpdated is java.util.Date (happens all the time with Hibernate). 
     isPropertySame = (help); 

    } else { 
     isPropertySame = pOriginal.equals(pUpdated); 
    } 

    if (!isPropertySame) { 
     PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated); 
     dao.save(delta); 
    } 
} 

¿Alguna idea sobre qué podría poner (ayudar)?

+1

El problema con 'Timestamp' y' Date' es un problema bien conocido: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4631234. Se ha corregido en Java 6 (probablemente en 5u6 también). – notnoop

+0

¿Se supone que las variables "originalValue" y "updatedValue" son "pOriginal" y "pUpdated", respectivamente? – erickson

+0

Me alegro de que se haya solucionado la confusión Timestamp/Date. Me temo que no me ayuda, ya que estoy estancado en 1.5, pero es bueno saberlo para el futuro. –

Respuesta

0

Bueno, dado que no pude encontrar una manera "pura" de hacer esto, y el hecho de que seguí corriendo en casos de esquina (como, además de esto, la dificultad de manejar propiedades que son colecciones), Decidí hacer mi método generador de delta manera más tonto. Me di cuenta de que solo estoy probando 9 tipos diferentes de objetos, así que solo puedo probar cuál de los 9 objetos comparo, luego lanzarlo a ese objeto y realizar pruebas específicas de cada objeto.

La implementación de esta manera tomó aproximadamente una hora, y aunque tengo que volver a compilar cada vez que cambian los objetos, me imagino que todavía estoy en negro, incluso si me paso días en este mantenimiento.

Así que, al final, supongo que la respuesta es que no hay respuesta. Los genéricos de Java se implementan de tal manera que es imposible evitar ocasionalmente la supresión de las advertencias del compilador y las excepciones de riesgo de clase de ejecución en tiempo de ejecución.

4

Esto me parece que lo hago de la manera difícil. Usted puede tener sus granos de implementar comparables, en cuyo caso simplemente compararlos directamente o crear un comparador -

public class Bean implements Comparable<Bean> {...} 

    public int compareTo(Bean other){ ... } 
} 

o

public int compare(Bean a, Bean b){ 
    Comparator<Bean> c = new Comparator<Bean>(){ 
    public int compareTo(Bean a, Bean b){ ... } 
    public boolean equals(Object o){.. } 
}; 
    return c.compare(a, b); 
} 

Estoy de acuerdo con usted en que los genéricos de Java puede ser un poco , eh ... complicado.

+0

¡Gracias por su ayuda! Lo siento, pero simplifiqué demasiado mi ejemplo original hasta el punto en que los comparadores específicos de clase funcionarían. He actualizado el ejemplo para reflejar con mayor precisión mi requisito de recopilar registros delta por propiedad que ha cambiado. También puede imaginarse que necesitaría realizar una exploración profunda de un gráfico de objeto muy grande para generar muchos de tales registros delta. –

2

No acabo de ver lo que está mal con simplemente haciendo lo siguiente:

MyObject original = getOriginal(); 
MyObject updated = getUpdated(); 
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) { 
    // Assume I'm putting in the try/catch block 
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null); 
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null); 

    boolean isPropertySame; 
    if (pOriginal instanceof Comparable) { 
     @SuppressWarnings("unchecked") 
     Comparable<Object> originalValue = (Comparable<Object>) pOriginal; 
     @SuppressWarnings("unchecked") 
     Comparable<Object> updatedValue = (Comparable<Object>) pUpdated; 
     isPropertySame = originalValue.compareTo(updatedValue) == 0; 
    } else { 
     isPropertySame = pOriginal.equals(pUpdated); 
    } 

    if (!isPropertySame) { 
     PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated); 
     dao.save(delta); 
    } 
} 

Usando Class.cast en su caso, realmente no ayudan en absoluto con la seguridad de tipos.

+0

Estoy intentando evitar hacer esto, ya que arroja una advertencia: "Comparable es un tipo sin procesar. Las referencias al tipo genérico Comparable deben parametrizarse". Ahora, podría simplemente poner @SuppressWarnings en la parte superior de mi método para hacer que esto desaparezca, pero esperaba una respuesta "pura". Esta solución evita los genéricos por completo. Sin embargo, tiene toda la razón sobre el uso de cast(). Es exactamente tan frágil como el molde en su solución. La única razón por la que está ahí es porque estaba tratando de hacer una reflexión/manipulación de tipo en tiempo de ejecución. –

+0

Dado que el genérico se borra durante el tiempo de ejecución, el genérico no lo ayuda en absoluto. Acabo de modificar el código para hacer que el código sea más "genérico". Es más importante hacer que el código sea legible que satisfacer al compilador cuando no proporciona ningún tipo de seguridad de tipo adicional. – notnoop

+0

¿No es posible, en tiempo de ejecución, introspectar el parámetro de tipo (es decir, la "X" en Comparable )? Pensé que podrías hacer algo como: comparable.getClass(). GetTypeParameters() [0] .getGenericDeclaration(). ¿Bonita? Definitivamente no. Dado que todo el código en este método fue escrito en Java 1.5, pensé que sería posible escribir algo que no arroje advertencias/errores, y es seguro, sin tener que suprimir nada. ¿Es esto un sueño? –

1

Parece que la suposición es que si una clase implementa Comparable, el parámetro tipo es la clase misma. Es decir, "class X implements Comparable<X>". Si ese es el caso, entonces tiene sentido decir,

X a = new X(1), b = new X(2); 
a.compareTo(b); 

Sin embargo, es definitivamente posible definir una clase como "class X implements Comparable<Y>". Entonces uno podría intentar algo como esto & hellip;

X a = new X(1), b = new X(2); 
a.compareTo((Y) b); 

& hellip; pero claramente, se generaría un ClassCastException porque b no es una instancia de Y.

Por lo tanto, la advertencia es válida. El código, que utiliza tipos sin procesar, no es seguro para tipos, y podría generar excepciones en tiempo de ejecución.

+0

El problema aquí es que no conoce los tipos de propiedades Bean hasta el tiempo de ejecución. No se puede saber cuál es el argumento tipo de 'Comparable' de ninguna manera conveniente. – notnoop

+0

@erickson Estoy totalmente de acuerdo con su análisis. Sin embargo, ¿no debería haber una forma de lograr la conversión correcta, en tiempo de ejecución, para poder probar si dos objetos son comparables entre sí y luego realizar esa comparación? Dado todos los métodos de tiempo de ejecución que están disponibles para nosotros, parece que debería poder hacer esto ... –