Cosa c'è di sbagliato con i generici di Java? [chiuso]

46

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

    
posta Michael K 30.11.2010 - 21:41
fonte

7 risposte

50

L'implementazione generica di Java utilizza cancellazione dei tipi . Ciò significa che le tue raccolte generiche strongmente tipizzate sono effettivamente di tipo Object in fase di esecuzione. Questo ha alcune considerazioni sulle prestazioni in quanto significa che i tipi primitivi devono essere racchiusi quando aggiunti a una raccolta generica. Ovviamente i vantaggi della correttezza del tipo a tempo di compilazione superano la stupidità generale della cancellazione dei tipi e l'attenzione ossessiva sulla retrocompatibilità.

    
risposta data 30.11.2010 - 21:54
fonte
26

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

Non c'è nulla di sbagliato nei generici di Java per quanto riguarda il linguaggio Java. Come descritto in C # vs generici Java , i generici di Java sono praticamente adatti al livello linguistico 1 .

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 che erano disponibili in Java-the-language
  • è coinvolta una penalizzazione delle prestazioni

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

1 Con la possibile eccezione di caratteri jolly, si ritiene che il tipo di inferenza possa essere indecidibile nel caso generale. Questa è una grande differenza tra C # e generici Java che non è menzionata molto spesso. Grazie, antimonio.

    
risposta data 01.12.2010 - 04:12
fonte
13

La solita critica è la mancanza di reificazione. Vale a dire che gli oggetti in fase di esecuzione non contengono informazioni sui loro argomenti generici (sebbene le informazioni siano ancora presenti su campi, metodi, costruttori e classi e interfacce estese). Ciò significa che puoi trasmettere, ad esempio, ArrayList<String> a List<File> . Il compilatore ti fornirà degli avvisi, ma ti avviserà anche se prendi un ArrayList<String> assegnandolo a un riferimento Object e quindi lo lanci a List<String> . L'aspetto positivo è 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>) ). Devi invece usare nomi di metodi migliori. Si noti che questo sovraccarico non richiederebbe la reificazione, poiché l'overloading è un problema statico in fase di 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-ask, il lato dichiarazione piuttosto che i generici lato-utilizzo sarebbero più appropriati.

    
risposta data 30.11.2010 - 23:08
fonte
10

I generici di 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 avresti bisogno di macellare ogni classe nel codice precedente per farlo funzionare come dovrebbe se i generici fossero in realtà parametri di classe generici.

    
risposta data 10.02.2011 - 05:13
fonte
5
  • gli avvisi del compilatore per i parametri generici mancanti che non servono a niente rendono la lingua inutilmente dettagliata, ad esempio public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics non giocare con gli array

  • Le informazioni sul tipo perduto fanno riflettere un casino di casting e di nastro adesivo.

risposta data 10.02.2011 - 05:59
fonte
5

Penso che altre risposte lo abbiano detto in una certa misura, ma non molto chiaramente. Uno dei problemi con i generici sta perdendo i 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 sottile differenza, ma è importante. Poiché arr è creato in fase di runtime, i tipi generici vengono cancellati in fase di esecuzione, quindi non è possibile capirlo. Come alcuni hanno dichiarato che ArrayList<Integer> è uguale a ArrayList<String> dal punto di vista della riflessione.

Questo potrebbe non essere importante per l'utente di Generics, ma diciamo che volevamo creare qualche quadro 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 un factory generico per creare un'istanza di MySpecialObject perché questo è il tipo generico concreto che abbiamo dichiarato per questa istanza. Beh, la classe Factory non può interrogarsi per scoprire il tipo concreto dichiarato per questa istanza perché Java li ha cancellati.

Nei generici di .Net è possibile farlo perché in fase di esecuzione l'oggetto sa che sono tipi generici perché il compilatore lo ha eseguito nel binario. Con le cancellature Java non può farlo.

    
risposta data 01.12.2010 - 03:51
fonte
1

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 gli array, ma questo è stato menzionato.

Un grande fastidio psicologico: di tanto in tanto mi capita in situazioni in cui i farmaci generici non possono essere fatti funzionare. (Gli array sono l'esempio più semplice.) E non riesco a capire se i generici non possono fare il lavoro o se sono semplicemente stupidi. Lo odio. Qualcosa come i farmaci generici dovrebbero funzionare sempre. Ogni volta che non posso fare quello che voglio usando Java-the-language, so che il problema sono io, e so che se continuerò a spingere, ci arriverò alla fine. Con i farmaci generici, se divento troppo persistente, posso perdere un sacco di tempo.

Ma il vero problema è che i generici aggiungono troppa complessità a troppi guadagni. Nei casi più semplici può impedirmi di aggiungere una mela a una lista contenente auto. Belle. Ma senza generici questo errore genererebbe una ClassCastException molto veloce in fase di esecuzione con poco spreco di tempo. Se aggiungo un'auto con un seggiolino per bambini con un bambino, ho bisogno di un avvertimento in fase di compilazione che l'elenco sia solo per le auto con seggiolini per bambini che contengono scimpanzé per bambini? Un elenco di istanze di oggetti semplici inizia ad apparire come una buona idea.

Il codice generico può avere molte parole e caratteri e occupare molto spazio in più e richiedere molto più tempo per essere letto. Posso passare molto tempo a far funzionare tutto quel codice in più. Quando succede, 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. Mi piacerebbe poterlo consegnare per la manutenzione a persone che sono meno intelligenti di me o che hanno meno tempo da perdere.

D'altra parte (mi sento un po 'ferito lassù e sento il bisogno di bilanciare), è bello quando si usano raccolte e mappe semplici per aggiungere, mettere e ottenere controlli quando scritto, e di solito non aggiunge molta complessità al codice ( se qualcuno else scrive la raccolta o la mappa) . E Java è migliore di C #. Sembra che nessuna delle collezioni C # che voglio utilizzare gestisca mai i generici. (Ammetto di avere gusti strani nelle raccolte.)

    
risposta data 09.02.2012 - 23:04
fonte

Leggi altre domande sui tag