it-swarm.it

Cosa c'è di sbagliato con i generici di Java?

Ho visto diverse volte su questo sito post che denigrano l'implementazione di generici da parte di Java. Ora posso onestamente dire che non ho avuto problemi con il loro utilizzo. Tuttavia, non ho tentato di creare una classe generica da solo. Quindi, quali sono i tuoi problemi con il supporto generico di Java?

50
Michael K

L'implementazione generica di Java utilizza tipo di cancellazione . Ciò significa che le tue raccolte generiche fortemente tipizzate sono in realtà di tipo Object in fase di esecuzione. Questo ha alcune considerazioni sulle prestazioni in quanto significa che i tipi primitivi devono essere inscatolati quando aggiunti a una raccolta generica. Naturalmente i vantaggi della correttezza del tipo di tempo di compilazione superano la stupidità generale della cancellazione del tipo e della focalizzazione ossessiva sulla compatibilità con le versioni precedenti.

56
ChaosPandion

Osservare che le risposte che sono già state fornite si concentrano sulla combinazione di Java-the-language, JVM e la libreria di classi Java Class.

Non c'è niente di sbagliato nei generici di Java per quanto riguarda Java-the-language. Come descritto in C # vs Java generics , i generici di Java sono praticamente a livello linguistico1.

La cosa non ottimale è che la JVM non supporta direttamente i generici, il che ha diverse conseguenze importanti:

  • le parti relative alla riflessione della Libreria di classi non possono esporre le informazioni complete sul tipo disponibili in Java-the-language
  • è prevista una penalità per le prestazioni

Suppongo che la distinzione che sto facendo sia pedante poiché Java-the-language è ubiquamente compilato con JVM come target e Java Class Library come libreria principale.

1 Con la possibile eccezione dei caratteri jolly, che si ritiene rendere l'inferenza del tipo indecidibile nel caso generale. Questa è una grande differenza tra C # e Java che non sono menzionati molto spesso. Grazie, Antimonio.

26
Roman Starkov

La solita critica è la mancanza di reificazione. Vale a dire gli oggetti in fase di runtime non contengono informazioni sui loro argomenti generici (sebbene le informazioni siano ancora presenti su campi, metodo, costruttori e classe estesa e interfacce). Questo significa che potresti lanciare, diciamo, ArrayList<String> In List<File>. Il compilatore ti avvertirà, ma ti avvertirà anche se prendi un ArrayList<String> Assegnandolo a un riferimento Object e poi lo lanci in List<String>. I lati positivi sono che con i generici probabilmente non dovresti fare il casting, le prestazioni sono migliori senza i dati non necessari e, ovviamente, la compatibilità con le versioni precedenti.

Alcune persone si lamentano del fatto che non è possibile sovraccaricare sulla base di argomenti generici (void fn(Set<String>) e void fn(Set<File>)). È necessario invece utilizzare nomi di metodi migliori. Si noti che questo sovraccarico non richiederebbe la reificazione, poiché il sovraccarico è un problema statico durante la compilazione.

I tipi primitivi non funzionano con i generici.

I caratteri jolly e i limiti sono piuttosto complicati. Sono molto utili Se Java preferiva l'immutabilità e le interfacce Tell-Don't-Ask, allora i generici sul lato della dichiarazione piuttosto che sul lato dell'uso sarebbero più appropriati.

13

I generici Java fanno schifo perché non puoi fare quanto segue:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

Non è necessario macellare ogni classe nel codice sopra per farlo funzionare come dovrebbe se i generici fossero in realtà parametri di classe generici.

10
davidk01
  • gli avvertimenti del compilatore per i parametri generici mancanti che non servono a nulla rendono la lingua inutilmente dettagliata, ad esempio public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics non giocare a Nice con le matrici

  • Le informazioni perse sul tipo rendono la riflessione un casino di casting e nastro adesivo.

5
Steve B.

Penso che altre risposte lo abbiano detto in una certa misura, ma non molto chiaramente. Uno dei problemi con i generici è la perdita dei tipi generici durante il processo di riflessione. Quindi per esempio:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Sfortunatamente, i tipi restituiti non possono dirti che i tipi generici di arr sono String. È una differenza sottile, ma è importante. Poiché arr viene creato in fase di esecuzione, i tipi generici vengono cancellati in fase di esecuzione, quindi non è possibile capirlo. Come alcuni hanno affermato ArrayList<Integer> ha lo stesso aspetto di ArrayList<String> dal punto di vista della riflessione.

Questo potrebbe non essere importante per l'utente di Generics, ma diciamo che volevamo creare un framework di fantasia che usasse la riflessione per capire cose fantasiose su come l'utente ha dichiarato i tipi generici concreti di un'istanza.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Diciamo che volevamo che una fabbrica generica creasse un'istanza di MySpecialObject perché quello è il tipo generico concreto che abbiamo dichiarato per questa istanza. Bene, la classe Factory non può interrogarsi per scoprire il tipo concreto dichiarato per questa istanza perché Java li ha cancellati.

Nei generici di .Net puoi farlo perché in fase di esecuzione l'oggetto sa che sono tipi generici perché il compilatore lo ha rispettato nel file binario. With erasures Java non può farlo.

5
chubbsondubs

Potrei dire alcune buone cose sui generici, ma non era questa la domanda. Potrei lamentarmi che non sono disponibili in fase di esecuzione e non funzionano con le matrici, ma questo è stato menzionato.

Un grande fastidio psicologico: Occasionalmente mi imbatto in situazioni in cui i generici non possono essere fatti funzionare. (Le matrici sono l'esempio più semplice.) E non riesco a capire se i generici non possono fare il lavoro o se sono solo stupido. Lo odio. Qualcosa come i generici dovrebbe funzionare sempre. Ogni altra volta che non riesco a fare quello che voglio usando Java-the-language, conosco il problema sono io, e so se continuo a spingendo, ci arriverò alla fine. Con i generici, se divento troppo persistente, posso perdere molto tempo.

Ma il vero problema è che i generici aggiungono troppa complessità per un guadagno troppo piccolo. Nei casi più semplici può impedirmi di aggiungere un Apple a un elenco che contiene automobili. Bene. Ma senza generici questo errore genererebbe una ClassCastException davvero veloce in fase di esecuzione con poco tempo perso. Se io aggiungere un'auto con un seggiolino per bambini con dentro un bambino, ho bisogno di un avviso in fase di compilazione che l'elenco è solo per le auto con seggiolini per bambini che contengono scimpanzé per bambini? Un elenco di semplici istanze di oggetti inizia a sembrare una buona idea.

Il codice generico può contenere molte parole e caratteri e occupare molto spazio extra e impiegare molto più tempo a leggere. Posso passare molto tempo a far funzionare tutto quel codice extra. Quando lo fa, mi sento follemente intelligente. Ho anche perso diverse ore o più del mio tempo e devo chiedermi se qualcun altro sarà mai in grado di capire il codice. Vorrei poterlo consegnare per la manutenzione a persone che sono meno intelligenti di me o che hanno meno tempo da perdere.

D'altra parte (Sono rimasto un po 'bloccato lì e sento il bisogno di fornire un po' di equilibrio), è bello quando si usano raccolte e mappe semplici per ha aggiunto, mette e viene verificato quando scritto, e di solito non aggiunge molta complessità al codice ( if qualcuno altro scrive la raccolta o la mappa) . E Java è meglio di C #. Sembra che nessuna delle raccolte C # che voglio usare gestisca mai generici. (Ammetto di avere gusti strani nelle raccolte).

1
RalphChapin