2015-05-28 10 views
11

Siamo attualmente in fase di migrazione di un'applicazione da Java 7 a Java 8. Dopo aver risolto alcuni problemi di compilazione, mi sono imbattuto in un problema simile alla seguente domanda: ClassCast Error: Java 7 vs Java 8.Come rilevare chiamate di metodi ambigue che potrebbero causare ClassCastException in Java 8?

Per riassumere, ecco un esempio di codice che mostra il problema:

public class Test { 
    public static void main(String[] args) { 
     System.out.println(String.valueOf(getVal("xxx"))); // 7: prints the result, 8: Exception 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> T getVal(String param) { 
     // do some computation based on param... 
     return (T) result; // actual return type only depends on param so the caller knows what to expect 
    } 
} 

L'idea era che avremmo spinta che il chiamante conosce il tipo previsto, e questo lo eviterebbe un cast esplicito (I non dire che questa era una buona idea ...). In molti casi, il chiamante si aspetta solo un Object quindi non c'è stato alcun cast implicito.

Come indicato nella domanda precedente, l'esempio String.valueOf ha funzionato correttamente in Java 7 perché non era presente alcuna inferenza di tipo, pertanto è stato assunto Object. Ora in Java 8, il compilatore sceglie il tipo più specifico (qui char[]), che causa uno ClastCastException in fase di esecuzione.

Il problema è che abbiamo circa 350 chiamate di questo metodo getVal. C'è un modo per rilevare chiamate con metodo sovraccarico che differiscono tra Java 7 e Java 8? I.E. rileva quando il compilatore Java 8 seleziona un metodo diverso dal compilatore Java 7.

+2

Abbiamo avuto un metodo simile che è stato anche chiamato in centinaia di posti. L'ho sostituito introducendo un ulteriore parametro 'Classe clazz'. In questo modo puoi avere una gestione esplicita degli errori: restituire null o lanciare qualche eccezione più specifica. L'ho fatto manualmente spendendo mezz'ora. Ti consiglio di fare lo stesso. Non so come automatizzarlo, quindi questo non è qualificato come risposta alla tua domanda. –

+2

C'è una risposta semplice: * non sopprimere gli avvertimenti "deselezionati" *. Ti hanno già detto che il tuo codice è sbagliato. Se vuoi correggere il tuo codice ora che è troppo tardi, cerca le occorrenze di '@SuppressWarnings (" unchecked ")' ... – Holger

+0

@Holger Potresti dirlo al mio collega quando ha implementato questo nel 2011? ;-) Sapevo già che era una cattiva idea in primo luogo (anche se non così male come lo è ora in Java 8). Inoltre, non ci sono '@ SuppressWarnings' sulla chiamata al metodo, è solo nella dichiarazione di' getVal'. La rimozione di questa annotazione qui non mostrerà quindi nessuno degli usi problematici. –

risposta

2

Una migliore alternativa sarebbe:

public class Test { 
    public static void main(String[] args) { 
     System.out.println(String.valueOf(getVal("xxx"))); // 7: prints the result, 8: Exception 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> T getVal(T param) { 
     // do some computation based on param... 
     return param; // actual return type only depends on param so the caller knows what to expect 
    } 
} 

che lavorerà sia Java 7 e 8. Java

+0

Questa non è un'alternativa dato che imponi che il tipo restituito è lo stesso del parametro (che nel mio caso è un 'String' e non riesco a modificarlo con facilità). Inoltre, la domanda è identificare i casi in cui le chiamate al metodo sovraccarico sono diverse tra Java 7 e 8, per evitare di cambiarle tutte o cercarle a mano. –

1

Alla fine la soluzione era cambiare getVal() tornare Object:

public static Object getVal(String param) { 
     // do some computation based on param... 
     return result; 
    } 

e aggiungi un secondo metodo che accetta anche la classe desiderata come parametro:

quindi correggere tutti i problemi di compilazione (dove il chiamante non si aspettava Object) aggiungendo il parametro di classe appropriato.

L'aggiunta di un cast avrebbe funzionato, ma avrebbe causato molti avvisi per i cast incondizionati. Il cast incondizionato è effettivamente ancora lì (attraverso il parametro clazz), ma ciò consente di identificare facilmente tutti i chiamanti che hanno bisogno di un cast poiché usano il metodo con 2 parametri.

Inoltre - e questo è molto specifico per questo caso - è emerso che il param stesso era spesso il risultato di una chiamata di metodo su alcuni TypedParam<T> dove T era il tipo rendimento atteso di getVal(), e che conteneva anche la classe di T.

potrei quindi implementare un metodo ulteriore comodità:

public static <T> T getVal(TypedParam<T> param) { 
     return getVal(param.stringValue(), param.getValueClass()); 
    } 

e sostituito tutte getVal(param.stringValue()) semplicemente getVal(param).

Questa soluzione non risolve il caso generale - rilevare le chiamate metodo sovraccaricato che differiscono tra Java 7 e Java 8 - ma lo risolve per i metodi che sono noti per causare questo problema. E da allora non l'abbiamo trovato altrove.

Problemi correlati