Generici e cancellazione del tipo


10

I generici in Java sono implementati usando la cancellazione del tipo. JLS afferma che l'ispirazione era la compatibilità con le versioni precedenti. Dove come d'altra parte i generici C # sono riutilizzabili.

Teoricamente, quali sono i vantaggi e gli svantaggi di avere Generics come "cancellazione" o "riutilizzabile"?

Java manca su qualcosa?

Risposte:


8

Bene, la motivazione (retrocompatibilità) è sia un vantaggio che uno svantaggio. È uno svantaggio perché tutti preferiremmo avere tipi riutilizzabili, ma il prezzo da pagare era alto. Considera le scelte di progettazione in C #. Hanno tipi riutilizzabili, ma ora hanno API duplicate. Quindi, immagina un'API Java in cui avevamo anche API duplicate per ogni classe con parametri. Ora immagina di portare migliaia di righe di codice dalle classi legacy alle nuove classi generiche. Ora, chi non considererebbe le API duplicate uno svantaggio? Ma hey, hanno tipi riutilizzabili!

Quindi, la motivazione principale è stata "l'evoluzione, non la rivoluzione". E logicamente, ogni decisione ha dei compromessi.

Oltre agli altri svantaggi menzionati, potremmo anche aggiungere il fatto che la cancellazione del tipo può essere difficile da ragionare al momento della compilazione, poiché non è evidente che alcuni tipi verranno rimossi e questo porta a errori molto strani e difficili da trovare.

L'esistenza di metodi bridge (questo metodo generato sintatticamente dal compilatore per mantenere la compatibilità binaria) può anche essere vista come uno svantaggio. E questi possono essere precisamente uno dei motivi degli errori che ho citato nel paragrafo precedente.

I principali svantaggi derivano dal fatto già evidente che esiste una singola classe e non più classi per tipi generici. Come altro esempio, considera che il sovraccarico di un metodo con la stessa classe generica non riesce in Java:

public void doSomething(List<One>);
public void doSomething(List<Two>);

Qualcosa che potrebbe essere visto come uno svantaggio di tipi riutilizzabili (almeno in C #) è il fatto che causano l' esplosione del codice . Ad esempio List<int>è una classe e a List<double>è un'altra totalmente diversa, in quanto è a List<string>e a List<MyType>. Quindi le classi devono essere definite in fase di esecuzione, causando un'esplosione di classi e consumando risorse preziose mentre vengono generate.

Per quanto riguarda il fatto che non è possibile definire un new T()in Java, menzionato in un'altra risposta, è anche interessante considerare che questa non è solo una questione di cancellazione del tipo. Richiede anche l'esistenza di un costruttore predefinito, ecco perché C # richiede un "nuovo vincolo" per questo. (Vedi Perché il nuovo T () non è possibile in Java , di Alex Buckley).


3
Penso di aver ragione nel dire che sul CLR, per i tipi di riferimento T s, ottieni solo una copia del Class<T>codice per tutti i Ts; più una copia aggiuntiva per ogni tipo di valore T effettivamente utilizzato.
AakashM,

@AakashM: è corretto, esiste una singola classe di tipo di riferimento per generico, tuttavia ogni tipo di valore crea la propria versione. Ciò è dovuto alla fornitura di spazio di archiviazione specializzato in base al tipo, tutti i riferimenti occupano lo stesso spazio di archiviazione ma i tipi di valore occupano spazio di archiviazione diverso per tipo.
Guvante,

6

Il rovescio della medaglia della cancellazione del tipo è che non si conosce in fase di esecuzione il tipo di generico. Ciò implica che non è possibile applicare la riflessione su di essi e non è possibile istanziarli in fase di esecuzione.

Non puoi fare qualcosa del genere in Java:

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

C'è una soluzione alternativa per questo, ma richiede più codice. Il vantaggio, come è stato menzionato, è la compatibilità con le versioni precedenti.


3

Un altro vantaggio dei generici cancellati è che linguaggi diversi compilati per la JVM impiegano strategie diverse per i generici, ad esempio il sito di definizione di Scala contro la covarianza del sito di utilizzo di Java. Anche i generici di tipo superiore di Scala sarebbero stati più difficili da supportare sui tipi reificati di .Net, perché essenzialmente Scala su .Net ignorava il formato di reificazione incompatibile di C #. Se avessimo reificato i generici nella JVM, molto probabilmente quei generici reificati non sarebbero adatti per le caratteristiche che ci piacciono molto di Scala, e saremmo bloccati con qualcosa di non ottimale. Citando dal blog di Ola Bini ,

Ciò significa che se si desidera aggiungere generici reificati alla JVM, si dovrebbe essere certi che tale implementazione può comprendere sia tutti i linguaggi statici che vogliono fare innovazione nella propria versione di generici, sia tutti i linguaggi dinamici che vogliono creare una buona implementazione e una piacevole struttura di interfacciamento con le librerie Java. Perché se aggiungi generici reificati che non soddisfano questi criteri, soffocerai l'innovazione e renderai molto più difficile l'utilizzo di JVM come macchina virtuale multilingue.

Personalmente non considero la necessità di utilizzare un TypeTagcontesto associato a Scala per metodi sovraccarichi contrastanti come uno svantaggio, perché sposta l'overhead e l'inflessibilità della reificazione da un globale (intero programma e tutte le lingue possibili) a un sito di utilizzo per lingua problema che è solo il caso meno frequentemente.



Un altro motivo per favorire i generici cancellati è perché le dipendenze dovrebbero essere iniettate in un modo che rispetti la soluzione al Problema di espressione. Se stai testando casi specifici di tipi o codificando in fabbrica una factory nella tua funzione generica, allora stai facendo errori di estensibilità.
Shelby Moore III,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.