2009-08-06 15 views
7

Sto davvero cercando di amare i farmaci generici, ma finora i problemi che hanno causato superano qualsiasi beneficio. Per favore, per favore, mostrami che ho torto.Provare a confrontare, quindi confrontare

Capisco la necessità di aggiungere @SuppressWarnings ("deselezionato") quando si usano framework generici (Spring, Hibernate). Questo da solo riduce davvero il valore dei generici, così come le classi obbligatorie vengono passate nel costruttore per evitare le insidie ​​della cancellazione. Tuttavia, sembra che la vera spina sia lanciare. Di solito provo un po 'per ottenere la sintassi giusta, ma poi rinunciare al mio tentativo di purezza, aggiungere un @SuppressWarnings e andare avanti con la mia vita.

Ecco un esempio: sto riflettendo su un bean per cercare le differenze tra due istanze. Alcune proprietà implementano Comparable tale che (a.equals (b) == false) ma (a.compareTo (b) == 0) (ad es. BigDecimal, Date). In questi casi, voglio che la proprietà sia considerata la stessa.

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); 
    } 
} 

Tutte le idee su quello che ho potuto mettere in (aiuto)?

+1

Il problema con 'Timestamp' e' Date' è un problema ben noto: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4631234. È stato corretto in Java 6 (probabilmente anche in 5u6). – notnoop

+0

Le variabili "originalValue" e "updatedValue" dovrebbero essere "pOriginal" e "pUpdated", rispettivamente? – erickson

+0

Sono contento che sia stata corretta la confusione del timestamp/della data. Ho paura che non mi aiuti dato che sono bloccato al 1.5, ma è bello sapere per il futuro. –

risposta

0

Beh, visto che non ero in grado di trovare un modo "puro" per fare questo, e il fatto che ho continuato a correre in casi particolari (come, in aggiunta a questo, la difficoltà di gestire le proprietà che sono collezioni), Ho deciso di rendere il mio metodo di generazione delta modo più scemo. Mi sono reso conto che sto testando solo 9 tipi diversi di oggetti, quindi posso solo testare quale dei 9 oggetti sto confrontando, quindi eseguire il cast su quell'oggetto e fare test specifici dell'oggetto.

L'implementazione in questo modo è durata circa un'ora, e anche se devo ricompilare ogni volta che uno qualsiasi degli oggetti cambia, immagino di essere ancora in nero anche se trascorro giorni in questa manutenzione.

Quindi, alla fine, credo che la risposta sia che non c'è risposta. I generici Java sono implementati in modo tale che è impossibile evitare di sopprimere occasionalmente gli avvisi del compilatore e le eccezioni dei cast di classe run-time.

4

Questo mi sembra di andare sul serio. È possibile avere i fagioli implementare paragonabile, nel qual caso basta confrontarli direttamente, oppure creare un comparatore -

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); 
} 

sono d'accordo con te che i generici Java possono ottenere un po ' , ehm ... contorto.

+0

Grazie per il tuo aiuto! Mi dispiace, ma ho semplificato eccessivamente il mio esempio originale fino al punto in cui i comparatori specifici della classe avrebbero funzionato. Ora ho aggiornato l'esempio per riflettere in modo più accurato il mio requisito di raccogliere record delta per proprietà che è stata modificata. Si può inoltre immaginare che avrei bisogno di eseguire una scansione approfondita di un grafo di oggetti molto grande per generare molti di questi record delta. –

2

non ho ben vedere che cosa c'è di sbagliato con semplicemente facendo quanto segue:

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); 
    } 
} 

Utilizzando Class.cast nel tuo caso davvero non aiutano a tutti con la sicurezza di tipo.

+0

Sto cercando di evitare di fare esattamente questo, poiché lancia un avvertimento: "Comparabile è un tipo non elaborato. Riferimenti al tipo generico Comparabile deve essere parametrizzato". Ora, potrei semplicemente mettere un @SuppressWarnings nella parte superiore del mio metodo per farlo andare via, ma speravo in una risposta "pura". Questa soluzione evita completamente i farmaci generici. Hai assolutamente ragione sull'uso cast(), però. È esattamente fragile quanto il casting nella tua soluzione. L'unica ragione per cui è lì è perché stavo cercando di eseguire la riflessione/manipolazione del tipo run-time. –

+0

Dato che il generico viene cancellato in fase di esecuzione, generico non ti aiuta affatto. Ho appena modificato il codice per rendere il codice più "generico". È più importante rendere leggibile il codice piuttosto che soddisfare il compilatore quando non fornisce alcuna ulteriore sicurezza del tipo. – notnoop

+0

Non è possibile, in fase di runtime, analizzare il parametro del tipo (ad esempio "X" in Comparable )? Ho pensato che potevi fare qualcosa di simile: comparable.getClass(). GetTypeParameters() [0] .getGenericDeclaration(). Bella? Sicuramente no. Dato che tutto il codice in questo metodo è stato scritto in Java 1.5, ho pensato che sarebbe stato possibile scrivere qualcosa che non lanciasse avvisi/errori ed è sicuro per il tipo, senza dover sopprimere nulla. È un sogno irrealizzabile? –

1

Sembra che l'ipotesi sia che se una classe implementa Comparable, il parametro type è la classe stessa. Cioè "class X implements Comparable<X>". Se questo è il caso, allora ha senso dire,

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

Tuttavia, è sicuramente possibile definire una classe come "class X implements Comparable<Y>". Quindi si potrebbe tentare qualcosa del genere & hellip;

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

& hellip; ma chiaramente, un ClassCastException sarebbe sollevato perché b non è un'istanza di Y.

Quindi, l'avviso è valido. Il codice, utilizzando i tipi non elaborati, non è sicuro per il tipo e potrebbe generare eccezioni in fase di runtime.

+0

Il problema qui è che non si conoscono i tipi di proprietà Bean fino al runtime. Non puoi sapere quale sia l'argomento di tipo di Comparable in alcun modo conveniente. – notnoop

+0

@erickson Sono completamente d'accordo con la tua analisi. Tuttavia, non dovrebbe esserci un modo per ottenere il cast corretto - in fase di esecuzione - in modo da poter verificare se due oggetti sono confrontabili tra loro e quindi eseguire tale confronto? Considerati tutti i metodi di runtime disponibili, sembra che dovresti essere in grado di farlo ... –