it-swarm-eu.dev

Cosa c'è di bello nei generici, perché usarli?

Ho pensato di offrire questo softball a chiunque vorrebbe colpirlo fuori dal parco. Cosa sono i generici, quali sono i vantaggi dei generici, perché, dove, come dovrei usarli? Per favore, tienilo abbastanza semplice. Grazie.

80
MrBoJangles
  • Consente di scrivere codice/utilizzare metodi di libreria sicuri per i tipi, vale a dire che un elenco <stringa> è garantito come un elenco di stringhe.
  • A seguito dell'utilizzo dei generici, il compilatore può eseguire controlli in fase di compilazione sul codice per la sicurezza del tipo, ovvero stai cercando di inserire un int in quell'elenco di stringhe? L'uso di una ArrayList comporterebbe un errore di runtime meno trasparente.
  • Più veloce dell'uso di oggetti poiché evita il boxing/unboxing (dove .net deve convertire tipi di valore in tipi di riferimento o viceversa ) o eseguire il casting dagli oggetti al tipo di riferimento richiesto.
  • Consente di scrivere codice applicabile a molti tipi con lo stesso comportamento sottostante, ovvero un dizionario <string, int> utilizza lo stesso codice sottostante di un dizionario <DateTime, double>; usando generics, il team del framework doveva solo scrivere un pezzo di codice per ottenere entrambi i risultati con i vantaggi sopra menzionati.
120
ljs

Odio davvero ripetermi. Odio scrivere la stessa cosa più spesso di quanto devo. Non mi piace ripetere le cose più volte con lievi differenze.

Invece di creare:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

Posso creare una classe riutilizzabile ... (nel caso in cui non desideri utilizzare la raccolta non elaborata per qualche motivo)

class MyList<T> {
   T get(int index) { ... }
}

Ora sono 3 volte più efficiente e devo conservare solo una copia. Perché non vuoi mantenere meno codice?

Questo vale anche per le classi non di raccolta come un Callable<T> o a Reference<T> che deve interagire con altre classi. Vuoi davvero estendere Callable<T> e Future<T> e ogni altra classe associata per creare versioni sicure?

Io non.

46
James Schek

Non aver bisogno di eseguire il typecast è uno dei maggiori vantaggi di Java generics , poiché eseguirà il controllo del tipo al momento della compilazione- Questo ridurrà la possibilità di ClassCastExceptions che possono essere lanciati in fase di esecuzione e che possono portare a un codice più solido.

Ma sospetto che tu ne sia pienamente consapevole.

Ogni volta che guardo Generics mi fa venire il mal di testa. Trovo che la parte migliore di Java sia la sua semplicità e la sintassi minima e i generici non sono semplici e aggiungono una quantità significativa di nuova sintassi.

All'inizio, non ho visto nemmeno il beneficio dei generici. Ho iniziato a studiare Java dalla sintassi 1.4 (anche se Java 5 era fuori in quel momento) e quando ho incontrato generici, ho sentito che era più codice per scrivere, e davvero non ho capito i benefici.

Gli IDE moderni semplificano la scrittura di codice con generici.

Gli IDE più moderni e decenti sono abbastanza intelligenti da aiutare a scrivere codice con generici, specialmente con il completamento del codice.

Ecco un esempio di come creare un Map<String, Integer> Con un HashMap. Il codice che dovrei digitare è:

Map<String, Integer> m = new HashMap<String, Integer>();

E in effetti, è molto da scrivere solo per creare un nuovo HashMap. Tuttavia, in realtà, ho dovuto scrivere così tanto prima che Eclipse sapesse di cosa avevo bisogno:

Map<String, Integer> m = new Ha Ctrl+Space

È vero, dovevo selezionare HashMap da un elenco di candidati, ma fondamentalmente IDE sapeva cosa aggiungere, compresi i tipi generici. Con gli strumenti giusti, usare generics isn troppo male.

Inoltre, poiché i tipi sono noti, quando si recuperano elementi dalla raccolta generica, il IDE agirà come se quell'oggetto fosse già un oggetto del tipo dichiarato - non è necessario eseguire il casting per IDE per sapere qual è il tipo di oggetto.

Un vantaggio chiave dei generici deriva dal modo in cui gioca bene con le nuove funzioni Java 5. Ecco un esempio di lancio numeri interi in un Set e calcolando il suo totale:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

In quel pezzo di codice, ci sono tre nuove funzionalità Java 5 presenti:

Innanzitutto, i generici e l'autoboxing delle primitive consentono le seguenti righe:

set.add(10);
set.add(42);

L'intero 10 Viene autoboxato in un Integer con il valore di 10. (E lo stesso per 42). Quindi quel Integer viene lanciato nel Set che è noto per contenere Integers. Tentare di inserire un String provocherebbe un errore di compilazione.

Quindi, per for-each loop prende tutti e tre questi:

for (int i : set) {
  total += i;
}

Innanzitutto, i Set contenenti Integers vengono utilizzati in un ciclo for-each. Ogni elemento viene dichiarato come int e ciò è consentito poiché Integer è unboxed nella primitiva int. E il fatto che si verifichi questo unboxing è noto perché i generici sono stati usati per specificare che c'erano Integers presenti in Set.

I generici possono essere la colla che unisce le nuove funzionalità introdotte in Java 5, e rende la codifica più semplice e sicura. E il più delle volte gli IDE sono abbastanza intelligenti da aiutarti con buoni suggerimenti, quindi in genere non digiterà molto di più.

E francamente, come si può vedere dall'esempio Set, ritengo che l'utilizzo delle funzioni Java 5 possa rendere il codice più conciso e robusto.

Modifica - Un esempio senza generici

Il seguente è un esempio dell'esempio Set precedente senza l'uso di generici. È possibile, ma non è esattamente piacevole:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(Nota: il codice precedente genererà un avviso di conversione non controllato in fase di compilazione.)

Quando si utilizzano raccolte non generiche, i tipi immessi nella raccolta sono oggetti di tipo Object. Pertanto, in questo esempio, un Object è ciò che viene inserito add nel set.

set.add(10);
set.add(42);

Nelle righe sopra, è in gioco l'autoboxing: i valori primitivi int10 E 42 Sono stati autoboxati in Integer oggetti, che vengono aggiunti a il Set. Tuttavia, tieni presente che gli oggetti Integer vengono gestiti come Objects, poiché non ci sono informazioni sul tipo per aiutare il compilatore a sapere quale tipo dovrebbe aspettarsi il Set.

for (Object o : set) {

Questa è la parte cruciale. Il motivo per cui il ciclo for-each funziona è perché Set implementa l'interfaccia Iterable , che restituisce un Iterator con informazioni sul tipo, se presenti. (Iterator<T>, Cioè.)

Tuttavia, poiché non ci sono informazioni sul tipo, Set restituirà un Iterator che restituirà i valori in Set come Objects, ed è per questo l'elemento recuperato nel ciclo for-each must deve essere di tipo Object.

Ora che Object viene recuperato da Set, è necessario eseguire il cast manualmente su Integer per eseguire l'aggiunta:

  total += (Integer)o;

Qui, un typecast viene eseguito da Object a Integer. In questo caso, sappiamo che funzionerà sempre, ma la tipografia manuale mi fa sempre sentire che è un codice fragile che potrebbe essere danneggiato se viene apportata una modifica minore altrove. (Sento che ogni typecast è un ClassCastException in attesa di accadere, ma sto divagando ...)

Integer è ora decompresso in un int e può eseguire l'aggiunta nella variabile inttotal.

Spero di poter illustrare che le nuove funzionalità di Java 5 è possibile utilizzare con codice non generico, ma non è così semplice e chiaro come scrivere codice con generici. E , a mio avviso, per sfruttare appieno le nuove funzionalità di Java 5, si dovrebbe esaminare i generici, se non altro, consente controlli in fase di compilazione per impedire la digitazione non valida di generare eccezioni in fase di esecuzione.

20
coobird

Se dovessi cercare nel Java bug database appena prima della versione 1.5, avresti trovato sette volte più bug con NullPointerException rispetto a ClassCastException. Quindi non sembra che sia una grande caratteristica trovare bug, o almeno bug che persistono dopo un piccolo test del fumo.

Per me l'enorme vantaggio dei generici è che documentano nel codice informazioni importanti sul tipo. Se non volessi che le informazioni sul tipo fossero documentate nel codice, avrei usato un linguaggio tipizzato in modo dinamico, o almeno un linguaggio con un'inferenza di tipo più implicita.

Mantenere le raccolte di un oggetto su se stesso non è uno stile negativo (ma lo stile comune è quello di ignorare efficacemente l'incapsulamento). Dipende piuttosto da cosa stai facendo. Passare le raccolte agli "algoritmi" è leggermente più facile da controllare (in fase di compilazione o prima) con i generici.

15

Generici in Java facilitare polimorfismo parametrico . Tramite parametri di tipo, puoi passare argomenti ai tipi. Proprio come un metodo come String foo(String s) modella alcuni comportamento, non solo per una stringa particolare, ma per qualsiasi stringa s, quindi un tipo come List<T> modella un comportamento, non solo per un tipo specifico, ma per qualsiasi tipo . List<T> dice che per qualsiasi tipo T, esiste un tipo di List i cui elementi sono Ts . Quindi List è in realtà un costruttore di tipo . Prende un tipo come argomento e di conseguenza ne costruisce un altro.

Ecco un paio di esempi di tipi generici che uso ogni giorno. Innanzitutto, un'interfaccia generica molto utile:

public interface F<A, B> {
  public B f(A a);
}

Questa interfaccia dice che per alcuni due tipi, A e B, c'è una funzione (chiamata f) che accetta un A e restituisce un B. Quando si implementa questa interfaccia, A e B possono essere tutti i tipi desiderati, purché quando fornite una funzione f che accetta la prima e restituisce la seconda. Ecco un esempio di implementazione dell'interfaccia:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Prima dei generici, il polimorfismo è stato ottenuto dalla sottoclasse usando la parola chiave extends. Con i generici, possiamo effettivamente eliminare la sottoclasse e usare invece il polimorfismo parametrico. Ad esempio, considera una classe con parametri (generica) utilizzata per calcolare i codici hash per qualsiasi tipo. Invece di sovrascrivere Object.hashCode (), utilizzeremmo una classe generica come questa:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

Questo è molto più flessibile dell'utilizzo dell'ereditarietà, perché possiamo rimanere sul tema dell'uso della composizione e del polimorfismo parametrico senza bloccare le fragili gerarchie.

I generici di Java non sono perfetti però. Puoi astrarre sui tipi, ma non puoi astrarre sui costruttori di tipi, ad esempio. Cioè, puoi dire "per qualsiasi tipo T", ma non puoi dire "per qualsiasi tipo T che accetta un parametro di tipo A".

Ho scritto un articolo su questi limiti di Java generics, qui.

Una grande vittoria con i generici è che ti permettono di evitare la sottoclasse. La sottoclasse tende a creare fragili gerarchie di classi che sono difficili da estendere e classi che sono difficili da capire individualmente senza guardare l'intera gerarchia.

Prima che i generici potessero avere classi come Widget estese di FooWidget, BarWidget e BazWidget, con i generici puoi avere una singola classe generica Widget<A> che richiede Foo, Bar o Baz nel suo costruttore per darti Widget<Foo>, Widget<Bar>, e Widget<Baz>.

10
Apocalisp

I generici evitano il successo delle prestazioni di boxe e unboxing. Fondamentalmente, guarda ArrayList vs List <T>. Entrambi fanno le stesse cose principali, ma List <T> sarà molto più veloce perché non è necessario eseguire il box da/verso l'oggetto.

8
Darren Kopp
  • Collezioni tipizzate: anche se non si desidera utilizzarle, è probabile che sia necessario gestirle da altre librerie, altre fonti.

  • Digitazione generica nella creazione di classi:

    public class Foo <T> {public T get () ...

  • Evitare il casting - Non ho mai apprezzato cose del genere

    nuovo comparatore {public int compareTo (Object o) {if (o instanceof classIcareAbout) ...

Dove essenzialmente stai verificando una condizione che dovrebbe esistere solo perché l'interfaccia è espressa in termini di oggetti.

La mia reazione iniziale ai generici è stata simile alla tua: "troppo disordinata, troppo complicata". La mia esperienza è che dopo averli usati per un po 'ci si abitua e il codice senza di essi sembra meno chiaramente specificato e solo meno comodo. A parte questo, il resto del mondo Java li usa, quindi alla fine dovrai avere il programma, giusto?

5
Steve B.

Per fare un buon esempio. Immagina di avere una classe chiamata Foo

public class Foo
{
   public string Bar() { return "Bar"; }
}

Esempio 1 Ora vuoi avere una collezione di oggetti Foo. Hai due opzioni, LIst o ArrayList, che funzionano entrambe in modo simile.

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

Nel codice sopra, se provo ad aggiungere una classe di FireTruck anziché Foo, ArrayList lo aggiungerà, ma l'Elenco generico di Foo genererà un'eccezione.

Esempio due.

Ora hai i tuoi due elenchi di array e vuoi chiamare la funzione Bar () su ciascuno. Poiché hray ArrayList è pieno di oggetti, devi lanciarli prima di poter chiamare la barra. Ma poiché l'elenco generico di Foo può contenere solo Foos, puoi chiamare Bar () direttamente su quelli.

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}
5
Peter Lange

Mi piacciono solo perché ti danno un modo rapido per definire un tipo personalizzato (come li uso comunque).

Quindi, ad esempio, invece di definire una struttura composta da una stringa e un numero intero, e quindi dover implementare un intero insieme di oggetti e metodi su come accedere a una matrice di tali strutture e così via, puoi semplicemente creare un dizionario

Dictionary<int, string> dictionary = new Dictionary<int, string>();

E il compilatore/IDE fa il resto del sollevamento pesante. Un dizionario in particolare consente di utilizzare il primo tipo come chiave (nessun valore ripetuto).

5
Tom Kidd

Il miglior vantaggio per Generics è il riutilizzo del codice. Supponiamo che tu abbia molti oggetti business e che scriverai un codice MOLTO simile per ogni entità per eseguire le stesse azioni. (Operazioni I.E Linq to SQL).

Con generics, puoi creare una classe che sarà in grado di operare in base a uno dei tipi che ereditano da una data classe di base o implementare una determinata interfaccia in questo modo:

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

Notare il riutilizzo dello stesso servizio dati i diversi tipi nel metodo DoSomething sopra. Davvero elegante!

Ci sono molte altre ottime ragioni per usare i generici per il tuo lavoro, questo è il mio preferito.

5
Dean Poulin

Non hai mai scritto un metodo (o una classe) in cui il concetto chiave del metodo/classe non fosse strettamente legato a uno specifico tipo di dati dei parametri/variabili di istanza (pensa a elenco collegato, funzioni max/min, ricerca binaria , eccetera.).

Non hai mai desiderato poter riutilizzare l'algorthm/code senza ricorrere al riutilizzo incolla o compromettere la digitazione forte (ad esempio, voglio un List di stringhe, non un List di cose che spero sono stringhe!)?

Ecco perché dovresti vuoi usare i generici (o qualcosa di meglio).

4
Bert F

Non dimenticare che i generici non sono solo usati dalle classi, ma possono anche essere usati dai metodi. Ad esempio, prendi il seguente frammento:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

È semplice, ma può essere utilizzato in modo molto elegante. La cosa bella è che il metodo restituisce qualunque cosa sia stata data. Questo aiuta quando si gestiscono le eccezioni che devono essere restituite al chiamante:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

Il punto è che non perdi il tipo passandolo attraverso un metodo. Puoi lanciare il tipo corretto di eccezione invece di solo un Throwable, che sarebbe tutto ciò che potresti fare senza generici.

Questo è solo un semplice esempio di un uso per metodi generici. Ci sono alcune altre cose interessanti che puoi fare con i metodi generici. Il più cool, secondo me, è il tipo inferendo con i generici. Prendi il seguente esempio (tratto da Effective Java) di Josh Bloch:

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Questo non fa molto, ma riduce un po 'di confusione quando i tipi generici sono lunghi (o nidificati; cioè Map<String, List<String>>).

3
jigawot

Dal sole Java, in risposta a "perché dovrei usare i generici?":

"Generics fornisce un modo per comunicare il tipo di una raccolta al compilatore, in modo che possa essere controllato. Una volta che il compilatore conosce il tipo di elemento della raccolta, il compilatore può verificare che la raccolta sia stata utilizzata in modo coerente e può inserire i cast corretti sui valori che vengono estratti dalla raccolta ... Il codice che utilizza generics è più chiaro e più sicuro .... il compilatore può verificare in fase di compilazione che i vincoli di tipo non vengano violati in fase di esecuzione [enfasi mia]. Poiché il programma viene compilato senza avvisi, possiamo affermare con certezza che non genererà una ClassCastException in fase di esecuzione. L'effetto netto dell'utilizzo dei generici, specialmente nei programmi di grandi dimensioni, è migliore leggibilità e robustezza. [enfasi mio] "

2
Demi

jvm esegue comunque il cast ... crea implicitamente il codice che tratta il tipo generico come "Oggetto" e crea cast all'istanza desiderata. Java i generici sono solo zucchero sintattico.

2
Anonymous Coward

Il vantaggio principale, come sottolinea Mitchel, è la tipizzazione forte senza la necessità di definire più classi.

In questo modo puoi fare cose come:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

Senza generici, dovresti lanciare blah [0] sul tipo corretto per accedere alle sue funzioni.

2
Kevin Pang

So che questa è una domanda C #, ma generics sono usati anche in altre lingue e il loro uso/obiettivi sono abbastanza simili.

Le raccolte Java usano generics since Java 1.5. Quindi, un buon posto per usarle è quando si crea il proprio oggetto simile a una raccolta.

Un esempio che vedo quasi ovunque è una classe Pair, che contiene due oggetti, ma ha bisogno di gestirli in modo generico.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

Ogni volta che usi questa classe Pair puoi specificare con quale tipo di oggetti vuoi che si occupi e qualsiasi tipo di problema cast verrà mostrato in fase di compilazione, piuttosto che in fase di esecuzione.

I generici possono anche avere i loro limiti definiti con le parole chiave "super" e "estende". Ad esempio, se vuoi trattare un tipo generico ma vuoi assicurarti che estenda una classe chiamata Foo (che ha un metodo setTitle):

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

Sebbene non sia molto interessante da solo, è utile sapere che ogni volta che gestisci un FooManager, sai che gestirà i tipi di MyClass e che MyClass estende Foo.

2
etchasketch

Li uso ad esempio in un GenericDao implementato con SpringORM e Hibernate che assomigliano a questo

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

Usando i generici, le mie implementazioni di questi DAO costringono lo sviluppatore a passare loro solo le entità per cui sono progettate semplicemente subclassando il GenericDao

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

Il mio piccolo framework è più robusto (ho cose come il filtro, il caricamento lento, la ricerca). Ho appena semplificato qui per darti un esempio

Come Steve e te, ho detto all'inizio "Troppo disordinato e complicato" ma ora vedo i suoi vantaggi

1
victor hugo

I generici ti consentono di creare oggetti fortemente tipizzati, ma non è necessario definire il tipo specifico. Penso che il miglior esempio utile sia l'Elenco e classi simili.

Usando la lista generica puoi avere una Lista delle liste qualunque cosa tu voglia e puoi sempre fare riferimento alla tipizzazione forte, non devi convertirti o cose come faresti con una matrice o una lista standard.

1
Mitchel Sellers

I generici ti consentono di utilizzare una tipizzazione forte per oggetti e strutture di dati che dovrebbero essere in grado di contenere qualsiasi oggetto. Elimina anche dattiloscritti noiosi e costosi quando si recuperano oggetti da strutture generiche (boxe/unboxing).

Un esempio che utilizza entrambi è un elenco collegato. A che cosa sarebbe utile una classe di elenco collegato se potesse usare solo l'oggetto Foo? Per implementare un elenco collegato in grado di gestire qualsiasi tipo di oggetto, l'elenco collegato e i nodi in una classe interna di nodo ipotetico devono essere generici se si desidera che l'elenco contenga solo un tipo di oggetto.

1
Steve Landey

Sono già menzionati evidenti benefici come "sicurezza del tipo" e "nessuna fusione", quindi forse posso parlare di altri "vantaggi" che spero possano essere d'aiuto.

Prima di tutto, la generica è un concetto indipendente dal linguaggio e, IMO, potrebbe avere più senso se si pensa allo stesso tempo al polimorfismo regolare (runtime).

Ad esempio, il polimorfismo, come sappiamo dalla progettazione orientata agli oggetti, ha una nozione di runtime in cui l'oggetto chiamante viene capito durante l'esecuzione mentre l'esecuzione del programma procede e il metodo pertinente viene chiamato di conseguenza in base al tipo di runtime. In generici, l'idea è in qualche modo simile ma tutto accade al momento della compilazione. Cosa significa e come lo usi?

(Atteniamoci a metodi generici per mantenerlo compatto) Significa che puoi ancora avere lo stesso metodo su classi separate (come hai fatto in precedenza in classi polimorfiche) ma questa volta sono generati automaticamente dal compilatore dipende dai tipi impostati al momento della compilazione. Parametrizzi i tuoi metodi sul tipo che dai al momento della compilazione. Quindi, invece di scrivere i metodi da zero per ogni singolo tipo che hai come fai nel polimorfismo di runtime (metodo di override), lasci che i compilatori facciano il lavoro durante la compilazione. Questo ha un ovvio vantaggio poiché non è necessario dedurre tutti i possibili tipi che potrebbero essere utilizzati nel sistema, il che lo rende molto più scalabile senza una modifica del codice.

Le lezioni funzionano più o meno allo stesso modo. Si parametrizza il tipo e il codice viene generato dal compilatore.

Una volta che hai avuto l'idea del "tempo di compilazione", puoi usare tipi "limitati" e limitare ciò che può essere passato come tipo parametrizzato attraverso classi/metodi. Quindi, puoi controllare cosa deve essere passato, il che è una cosa potente, specialmente se hai un framework consumato da altre persone.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

Nessuno può impostare sth diverso da MyObject ora.

Inoltre, è possibile "applicare" i vincoli di tipo sugli argomenti del metodo, il che significa che è possibile assicurarsi che entrambi gli argomenti del metodo dipendono dallo stesso tipo.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

Spero che tutto ciò abbia un senso.

1
stdout

Un altro vantaggio dell'utilizzo di Generics (soprattutto con raccolte/elenchi) è che si ottiene il controllo del tipo di tempo di compilazione. Ciò è molto utile quando si utilizza un Elenco generico anziché un Elenco di oggetti.

1

Se la tua raccolta contiene tipi di valore, non è necessario selezionare/deselezionare gli oggetti quando inseriti nella raccolta, quindi le tue prestazioni aumentano notevolmente. I fantastici componenti aggiuntivi come resharper possono generare più codice per te, come i cicli foreach.

1
gt124

La maggior parte della ragione è che forniscono Tipo di sicurezza

List<Customer> custCollection = new List<Customer>;

al contrario di

object[] custCollection = new object[] { cust1, cust2 };

come un semplice esempio.

1
Vin

In sintesi, i generici ti consentono di specificare più precisamente cosa intendi fare (digitazione più forte).

Questo ha diversi vantaggi per te:

  • Poiché il compilatore sa di più su ciò che vuoi fare, ti consente di omettere un sacco di tipi di casting perché sa già che il tipo sarà compatibile.

  • Questo ti dà anche un feedback precedente sulle correttezze del tuo programma. Cose che in precedenza non sarebbero riuscite in fase di esecuzione (ad es. Perché non è stato possibile eseguire il cast di un oggetto nel tipo desiderato), ora falliscono in fase di compilazione e puoi correggere l'errore prima che il tuo dipartimento di test registri un rapporto di bug criptico.

  • Il compilatore può fare più ottimizzazioni, come evitare il pugilato, ecc.

1
Thomas Danecker

Un paio di cose da aggiungere/espandere (parlando dal punto di vista di .NET):

I tipi generici consentono di creare classi e interfacce basate sui ruoli. Questo è già stato detto in termini più basilari, ma trovo che inizi a progettare il tuo codice con classi che sono implementate in modo agnostico - il che si traduce in codice altamente riutilizzabile.

Argomenti generici sui metodi possono fare la stessa cosa, ma aiutano anche ad applicare il principio "Tell Don't Ask" al casting, ovvero "dammi quello che voglio, e se non puoi, dimmi perché".

1
SpongeJim

L'uso dei generici per le raccolte è semplice e pulito. Anche se ci scommetti ovunque, il guadagno delle collezioni è una vittoria per me.

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vs

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

o

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

Questo da solo vale il "costo" marginale dei generici e non devi essere un Guru generico per usare questo e ottenere valore.

0
Will Hartung

Generics ti dà anche la possibilità di creare oggetti/metodi più riutilizzabili, fornendo comunque supporto specifico per tipo. In alcuni casi puoi anche ottenere molte prestazioni. Non conosco le specifiche complete su Java Generics, ma in .NET posso specificare i vincoli sul parametro Type, come Implements a Interface, Constructor e Derivation.

0
Eric Schneider

Una volta ho tenuto un discorso su questo argomento. Puoi trovare le mie diapositive, il mio codice e la mia registrazione audio su http://www.adventuresinsoftware.com/generics/ .

0
Michael L Perry