2010-10-16 15 views
6

(Si prega di non consigliare che avrei dovuto astratto più X e aggiungere un altro metodo per esso.)java: combinazione di instanceof e cast?

In C++, quando ho una variabile x di tipo e voglio fare qualcosa di specifico, se è anche di tipo Y* (Y essendo una sottoclasse di X), sto scrivendo questo: (? o è)

if(Y* y = dynamic_cast<Y*>(x)) { 
    // now do sth with y 
} 

La stessa cosa non sembra possibile in Java.

Ho letto questo codice Java invece:

if(x instanceof Y) { 
    Y y = (Y) x; 
    // ... 
} 

A volte, quando non si dispone di una variabile x ma è un'espressione più complessa, invece, proprio a causa di questo problema, è necessario una variabile dummy in Java:

X x = something(); 
if(x instanceof Y) { 
    Y y = (Y) x; 
    // ... 
} 
// x not needed here anymore 

(cosa comune è che something() è iterator.next() E ci si vede che anche voi non si può davvero chiamare che solo due volte Hai davvero bisogno la variabile fittizia...)

01.235.

Non hai davvero bisogno di x qui - lo hai solo perché non puoi fare il controllo instanceof in una sola volta con il cast. Confronti che, ancora una volta per il codice abbastanza comune C++:

if(Y* y = dynamic_cast<Y*>(something())) { 
    // ... 
} 

A causa di questo, ho introdotto una funzione castOrNull, che permette di evitare la variabile dummy x. Posso scrivere questo ora:

Y y = castOrNull(something(), Y.class); 
if(y != null) { 
    // ... 
} 

Attuazione castOrNull:

public static <T> T castOrNull(Object obj, Class<T> clazz) { 
    try { 
     return clazz.cast(obj); 
    } catch (ClassCastException exc) { 
     return null; 
    } 
} 

Ora, mi hanno detto che using this castOrNull function in that way is an evil thing do to. Perché? (O, per dirla alla questione più generale: Lei è d'accordo e anche che questo è il male Se sì, perché così O pensi che questo sia un valido (forse raro) caso d'uso???)


Come detto , Non voglio una discussione se l'uso di tale downcast sia una buona idea. Ma lasciatemi chiarire brevemente il motivo per cui a volte lo uso:

  1. A volte mi trovo in caso in cui devo scegliere tra l'aggiunta di un altro nuovo metodo per una cosa molto specifico (che si applicherà solo per un singolo sottoclasse in uno caso specifico) o utilizzando tale controllo instanceof. Fondamentalmente, ho la scelta tra l'aggiunta di una funzione doSomethingVeryVerySpecificIfIAmY() o il controllo instanceof. E in questi casi, sento che quest'ultimo è più pulito.

  2. A volte ho una raccolta di alcune interfacce/classe base e per tutte le voci di tipo Y, voglio fare qualcosa e quindi rimuoverle dalla raccolta. (Ad esempio, ho avuto il caso in cui ho avuto una struttura ad albero e volevo cancellare tutte bambino che sono foglie vuote.)

+1

userei _instanceOf_ invece di _class.cast_ al fine di evitare inutili eccezioni in _castOrNull_, ma a parte che ti sembra di avere perfettamente normale aiutante. Lascia che ** Tom ** chiarisca cosa intendeva. –

+0

Se Y è un _superclass_ di X, non hai bisogno di alcun cast - intendevi _subclass_? –

+0

Anche se questo non è un one-liner in Java, le prestazioni di instanceof e casting sono abbastanza buone. Inserisco un po 'di tempo in Java7 su diversi approcci al problema qui: http://stackoverflow.com/questions/16320014/java-optimization-nitpick-is-it-faster-to-cast-something-and-let-it- throw-excep/28858680 # 28858680 – Wheezil

risposta

8

Ora, mi è stato detto che l'uso di questa funzione castNNull in questo modo è una cosa malvagia. Perché?

mi viene in mente un paio di motivi:

  • E 'un modo oscuro e difficile di fare qualcosa di molto semplice. Il codice oscuro e complicato è difficile da leggere, difficile da mantenere, una potenziale fonte di errori (quando qualcuno non lo capisce) e quindi male.

  • Il modo oscuro e complicato che il metodo castOrNull funziona molto probabilmente non può essere ottimizzato dal compilatore JIT. Ti ritroverai con almeno 3 chiamate extra al metodo, oltre a un sacco di codice extra per eseguire il controllo del tipo e il cast in modo riflessivo. L'uso non necessario della riflessione è malvagio.

(Al contrario, il modo semplice (con instanceof seguita da un getto classe) utilizza bytecodes specifici per instanceof e categoria colata. Le sequenze di bytecode può quasi certamente saranno ottimizzati in modo che non v'è più di una nullo controllare e non più quel test del tipo di oggetto nel codice nativo.Questo è un modello comune che dovrebbe essere facile per il compilatore JIT per rilevare e ottimizzare.)

Naturalmente, "il male" è solo un altro modo di dicendo che VERAMENTE non dovresti farlo.

Nessuno dei due esempi aggiunti, utilizzare un metodo castOrNull necessario o desiderabile. IMO, il "modo semplice" è migliore sia dalla leggibilità che dalle prospettive di performance.

+0

Ah, grazie per quell'informazione! Quindi dovresti anche argomentare che non dovrei usare 'castOrNull'? O come valuteresti questi contro-punti contro i miei pro-punti? (Perché dopo tutto, evitare una variabile fittizia può anche portare a un codice più leggibile e più breve.) – Albert

+1

L'unico pro point che posso discernere è che in alcuni casi si evita di utilizzare una variabile temporanea. Per me, questo è un problema puramente cosmetico. L'uso di un codice temporaneo non rende il codice IMO meno leggibile, e anche il codice più breve non è un vantaggio di leggibilità. (E BTW non è una variabile "fittizia" perché è usata.) –

2

Non so esattamente perché la persona ha detto che era il male.Tuttavia, una possibilità per il loro ragionamento era il fatto che in seguito stavi catturando un'eccezione piuttosto che controllare prima di lanciare. Questo è un modo per farlo.

+0

Dubito che intendesse questo perché ho chiesto un codice migliore e il codice che ho postato qui era il suo suggerimento. – Albert

+0

Un'alternativa simile è stata discussa nei commenti alla risposta collegata e Tom non l'ha preferita rispetto alla variante sopra. –

+0

@Albert: mancate che ... visto che è il caso, direi che molto probabilmente è come dice la risposta di Péter, una questione di design subottimale. Ma davvero, per capire esattamente perché crede che sia sbagliato da fare, devi aspettare la sua risposta (se mai ti torna). – Thomas

4

IMHO il tuo castOrNull non è male, solo inutile. Sembra che tu sia ossessionato dall'eliminazione di una variabile temporanea e di una riga di codice, mentre per me la domanda più grande è perché hai bisogno di così tanti downcast nel tuo codice? In OO questo è quasi sempre un sintomo di design subottimale. E preferirei risolvere la causa principale invece di trattare il sintomo.

+1

Bene, ci sono molte librerie in Java che non progettiamo, ma dobbiamo usare. (L'iteratore menzionato è l'unico esempio) –

+0

@Nikita, abbastanza giusto, se si utilizza una libreria Java mal progettata ma allo stesso tempo indispensabile, difficilmente si hanno opzioni. La mia ipotesi personale è che tali librerie dovrebbero essere molto rare però. Se una libreria è così scomoda da usare, allora nessuno la usa, o prima o poi qualcuno inizia a sviluppare un'alternativa migliore. –

+0

Ho esteso la mia domanda un po 'per mostrare 2 casi in cui a volte lo uso. Non mi piacciono le regole rigide per davvero ** mai e poi mai * usare qualcosa e penso che i miei due casi forniti siano casi validi in cui potresti farlo in questo modo. Potresti essere più estremo di questo rispetto a me, ma immagino che anche tu non saresti così estremo da non usare mai un codice come questo. E poi, se ti capita di avere un caso del genere, mi piace sempre avere la soluzione più pulita ed è quello che sto chiedendo qui. :) – Albert

5

Nella maggior parte del codice Java scritto/progettato l'uso di instanceof e cast non avviene mai. Con l'aggiunta di generici non sono necessari molti casi di cast (e quindi instanceof). Lo fanno, a volte accadono ancora.

Il metodo castOrNull è malvagio in quanto si sta rendendo il codice Java "innaturale". Il problema più grande quando si passa da una lingua all'altra è l'adozione delle convenzioni della nuova lingua. Le variabili temporanee vanno bene in Java. In realtà tutto ciò che il tuo metodo sta facendo è nascondere la variabile temporanea.

Se stai scoprendo che stai scrivendo molti cast, devi esaminare il tuo codice e vedere perché e cercare i modi per rimuoverli. Ad esempio, nel caso in cui si menzioni l'aggiunta di un metodo "getNumberOfChildren", si consente di verificare se un nodo è vuoto e quindi in grado di sfoltirlo senza eseguire il casting (è un'ipotesi, potrebbe non funzionare in questo caso).

In genere, i cast sono "malvagi" in Java perché di solito non sono necessari. Il tuo metodo è più "malvagio" perché non è scritto nel modo in cui la maggior parte delle persone si aspetterebbe che Java venisse scritto.

Detto questo, se vuoi farlo, prova a farlo. Non è in realtà "malvagio", solo il modo "giusto" per farlo in Java.

0

Java Le eccezioni sono lente. Se stai cercando di ottimizzare le tue prestazioni evitando un cast doppio, ti stai sparando ai piedi usando le eccezioni al posto della logica. Mai fare affidamento su un'eccezione per qualcosa che potresti ragionevolmente verificare e correggere (esattamente quello che stai facendo).

How slow are Java exceptions?