Domanda

Ho pensato di offrire questa palla da softball a chiunque volesse lanciarla fuori dal parco.Cosa sono i generici, quali sono i vantaggi dei generici, perché, dove, come dovrei usarli?Per favore, mantienilo abbastanza semplice.Grazie.

È stato utile?

Soluzione

  • Consente di scrivere codice/utilizzare metodi di libreria indipendenti dai tipi, ad es.a List<string> è garantito che sia un elenco di stringhe.
  • Come risultato dell'utilizzo dei generici, il compilatore può eseguire controlli in fase di compilazione sul codice per l'indipendenza dal tipo, ad es.stai cercando di inserire un int in quell'elenco di stringhe?L'uso di un ArrayList comporterebbe un errore di runtime meno trasparente.
  • Più veloce rispetto all'utilizzo degli oggetti in quanto evita il boxing/unboxing (dove .net deve convertire tipi di valore ai tipi di riferimento o viceversa) o eseguire il cast dagli oggetti al tipo di riferimento richiesto.
  • Consente di scrivere codice applicabile a molti tipi con lo stesso comportamento sottostante, ad es.a Dictionary<string, int> utilizza lo stesso codice sottostante di Dictionary<DateTime, double>;utilizzando i generici, il team del framework ha dovuto scrivere solo un pezzo di codice per ottenere entrambi i risultati con i vantaggi sopra menzionati.

Altri suggerimenti

Odio davvero ripetermi.Odio scrivere la stessa cosa più spesso del necessario.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 per qualche motivo non desideri utilizzare la raccolta grezza)

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

Ora sono 3 volte più efficiente e devo mantenere solo una copia.Perché NON vorresti mantenere meno codice?

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

Io non.

Non aver bisogno del typecast è uno dei maggiori vantaggi dei generici Java, poiché eseguirà il controllo del tipo in fase di compilazione.Ciò ridurrà la possibilità di ClassCastExceptions che possono essere lanciati in fase di esecuzione e possono portare a un codice più robusto.

Ma sospetto che tu ne sia pienamente consapevole.

Ogni volta che guardo i generici mi dà 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 vedevo nemmeno i benefici dei farmaci generici.Ho iniziato a imparare Java dalla sintassi 1.4 (anche se all'epoca era uscito Java 5) e quando ho incontrato i generici, ho sentito che c'era più codice da scrivere e davvero non ne capivo i vantaggi.

Gli IDE moderni semplificano la scrittura del codice con i generici.

Gli IDE più moderni e decenti sono abbastanza intelligenti da assistere nella scrittura di codice con generici, in particolare con il completamento del codice.

Ecco un esempio di creazione di un file Map<String, Integer> con un HashMap.Il codice che dovrei digitare è:

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

E in effetti, c'è molto da scrivere solo per crearne uno nuovo HashMap.Tuttavia, in realtà, ho dovuto digitare solo questo prima che Eclipse sapesse di cosa avevo bisogno:

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

È vero, dovevo selezionare HashMap da un elenco di candidati, ma sostanzialmente l'IDE sapeva cosa aggiungere, compresi i tipi generici.Con gli strumenti giusti, l'uso dei generici non è poi così male.

Inoltre, poiché i tipi sono noti, quando si recuperano elementi dalla raccolta generica, l'IDE si comporterà come se quell'oggetto fosse già un oggetto del tipo dichiarato: non è necessario eseguire il casting affinché l'IDE conosca il tipo dell'oggetto È.

Un vantaggio chiave dei generici deriva dal modo in cui interagiscono bene con le nuove funzionalità di Java 5. Ecco un esempio di come inserire numeri interi in a Set e calcolandone il 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 sono presenti tre nuove funzionalità Java 5:

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

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

L'intero 10 è autoboxato in un file Integer con il valore di 10.(E lo stesso per 42).Allora quello Integer viene gettato nel Set che è noto contenere IntegerS.Cercando di lanciare a String causerebbe un errore di compilazione.

Successivamente, for-each loop li prende tutti e tre:

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

Prima il Set contenente Integers vengono utilizzati in un ciclo for-each.Ogni elemento è dichiarato essere an int e questo è consentito come Integer è unboxed riportato al primitivo int.E il fatto che avvenga questo unboxing è noto perché per specificare che c'erano è stato utilizzato il generico Integersi tiene nel Set.

I generici possono essere il collante che riunisce le nuove funzionalità introdotte in Java 5 e rende la codifica più semplice e sicura.E la maggior parte delle volte gli IDE sono abbastanza intelligenti da aiutarti con buoni suggerimenti, quindi in genere non sarà molto più necessario digitare.

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

Modifica: un esempio senza farmaci generici

Quello che segue è un esempio di quanto sopra Set esempio senza l’uso di farmaci 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 sopra 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, a Object è ciò che è addinserito nel set.

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

Nelle righe precedenti è in gioco l'autoboxing: il primitivo int valore 10 E 42 vengono inseriti automaticamente Integer oggetti, che vengono aggiunti al file Set.Tuttavia, tieni presente che Integer gli oggetti vengono gestiti come Objects, poiché non ci sono informazioni sul tipo che aiutino il compilatore a sapere di che tipo è il file Set dovrebbe aspettarsi.

for (Object o : set) {

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

Tuttavia, poiché non sono disponibili informazioni sul tipo, il file Set restituirà un Iterator che restituirà i valori in Set COME Objects, ed è per questo che l'elemento viene recuperato nel ciclo for-each dovere essere di tipo Object.

Ora che il Object viene recuperato da Set, deve essere lanciato su an Integer manualmente per eseguire l'aggiunta:

  total += (Integer)o;

In questo caso viene eseguito un typecast da an Object ad Integer.In questo caso, sappiamo che funzionerà sempre, ma il typecasting manuale mi fa sempre sentire che si tratta di un codice fragile che potrebbe essere danneggiato se viene apportata una modifica minore altrove.(Sento che ogni typecast è un ClassCastException in attesa che accada, ma sto divagando...)

IL Integer è ora unboxed in un int e ha permesso di eseguire l'aggiunta nel file int variabile total.

Spero di poter illustrare che le nuove funzionalità di Java 5 possono essere utilizzate con codice non generico, ma semplicemente non è così pulito e diretto come scrivere codice con codici generici.E, a mio avviso, per sfruttare appieno le nuove funzionalità di Java 5, si dovrebbe esaminare i generici, se almeno consentono controlli in fase di compilazione per impedire che typecast non validi generino eccezioni in fase di runtime.

Se cercassi nel database dei bug di Java appena prima del rilascio della versione 1.5, troverai sette volte più bug con NullPointerException di ClassCastException.Quindi non sembra che sia una grande funzionalità 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, utilizzerei un linguaggio tipizzato dinamicamente, o almeno un linguaggio con un'inferenza di tipo più implicita.

Mantenere per sé le raccolte di un oggetto non è uno stile negativo (ma lo stile comune è ignorare di fatto l'incapsulamento).Dipende piuttosto da cosa stai facendo.Passare le raccolte agli "algoritmi" è leggermente più semplice da controllare (in fase di compilazione o prima) con i generici.

I generici in Java facilitano polimorfismo parametrico.Tramite i parametri di tipo è possibile passare argomenti ai tipi.Proprio come un metodo simile String foo(String s) modella un comportamento, non solo per una stringa particolare, ma per qualsiasi stringa s, quindi un tipo come List<T> modella alcuni comportamenti, non solo per un tipo specifico, ma per qualsiasi tipo. List<T> Dillo per qualsiasi tipo T, c'è un tipo di List i cui elementi sono TS.COSÌ List è in realtà un costruttore di tipi.Prende un tipo come argomento e come risultato 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 lo dice per alcuni due tipi, A E B, c'è una funzione (chiamata f) che richiede un A e restituisce a B. Quando implementi questa interfaccia, A E B può essere qualsiasi tipo tu voglia, purché fornisca una funzione f che prende il primo e restituisce il secondo.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 veniva raggiunto da sottoclasse usando il extends parola chiave.Con i generici, possiamo effettivamente eliminare le sottoclassi e utilizzare invece il polimorfismo parametrico.Ad esempio, considera una classe parametrizzata (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 rispetto all'utilizzo dell'ereditarietà, perché possiamo restare sul tema dell'utilizzo della composizione e del polimorfismo parametrico senza bloccare fragili gerarchie.

I generici di Java però non sono perfetti.È possibile astrarre dai tipi, ma non è possibile astrarre dai 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 dei generici Java, qui.

Un grande vantaggio dei farmaci generici è che ti consentono di evitare le sottoclassi.La creazione di sottoclassi tende a creare gerarchie di classi fragili, difficili da estendere e classi difficili da comprendere individualmente senza considerare l'intera gerarchia.

Aree prima dei generici potresti avere classi simili Widget prorogato di FooWidget, BarWidget, E BazWidget, con i generics puoi avere una singola classe generica Widget<A> ci vuole un Foo, Bar O Baz nel suo costruttore per darti Widget<Foo>, Widget<Bar>, E Widget<Baz>.

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

Mi piacciono semplicemente perché ti danno un modo rapido per definire un tipo personalizzato (poiché 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 un array di quelle 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 lavoro pesante.Un Dizionario in particolare consente di utilizzare il primo tipo come chiave (nessun valore ripetuto).

Il miglior vantaggio per i generici è il riutilizzo del codice.Diciamo che hai molti oggetti business e scriverai un codice MOLTO simile per ciascuna entità per eseguire le stesse azioni.(IE Linq to operazioni SQL).

Con i generici, puoi creare una classe che sarà in grado di funzionare in base a uno qualsiasi dei tipi che ereditano da una determinata classe 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" });

    }
}

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

Ci sono molti altri ottimi motivi per utilizzare i generici per il tuo lavoro, questo è il mio preferito.

  • Raccolte digitate: anche se non vuoi usarle, probabilmente dovrai gestirle da altre biblioteche, da altre fonti.

  • Digitazione generica nella creazione della classe:

    classe pubblica foo <t> {public t get () ...

  • Evitare il casting: non mi sono sempre piaciute cose del genere

    nuovo comparatore {public int comparatE (oggetto o) {if (o istance of classicarEabout) ...

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

La mia reazione iniziale ai farmaci generici è stata simile alla tua: "troppo disordinato, troppo complicato".La mia esperienza è che dopo averli usati per un po' ci si abitua e il codice senza di essi sembra meno chiaramente specificato e semplicemente meno comodo.A parte questo, il resto del mondo Java li usa, quindi prima o poi dovrai iniziare con il programma, giusto?

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

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

Esempio 1Ora vuoi avere una raccolta 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 precedente, se provo ad aggiungere una classe di FireTruck invece di Foo, l'ArrayList la aggiungerà, ma l'elenco generico di Foo causerà la generazione di un'eccezione.

Esempio due.

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

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

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

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

Non hai mai desiderato di poter riutilizzare l'algoritmo/codice senza ricorrere al riutilizzo taglia e incolla o compromettere la digitazione forte (ad es.voglio un List di stringhe, non a List delle cose io Speranza sono stringhe!)?

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

Non dimenticare che i generici non vengono utilizzati solo dalle classi, ma possono essere utilizzati anche 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 gli sia stata data.Questo aiuta quando gestisci eccezioni che devono essere restituite al chiamante:

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

Il punto è che non si perde il tipo passandolo attraverso un metodo.Puoi lanciare il tipo corretto di eccezione anziché solo a Throwable, che sarebbe tutto ciò che potresti fare senza i generici.

Questo è solo un semplice esempio di utilizzo dei metodi generici.Ci sono molte altre cose interessanti che puoi fare con metodi generici.La cosa più interessante, secondo me, è l'inferenza di tipo con i generici.Prendiamo il seguente esempio (tratto da Effective Java 2nd Edition 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' il disordine quando i tipi generici sono lunghi (o nidificati;cioè. Map<String, List<String>>).

Il vantaggio principale, come sottolinea Mitchell, è 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 i generici, dovresti eseguire il cast di blah[0] sul tipo corretto per accedere alle sue funzioni.

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

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

Utilizzo delle raccolte Java generici da Java 1.5.Quindi, un buon posto per usarli è quando crei il tuo oggetto simile a una raccolta.

Un esempio che vedo quasi ovunque è una classe Pair, che contiene due oggetti, ma deve 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 problema di cast del tipo verrà visualizzato in fase di compilazione, anziché in fase di esecuzione.

I generici possono anche avere i propri limiti definiti con le parole chiave "super" ed "extends".Ad esempio, se vuoi gestire 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 di per sé, è utile sapere che ogni volta che hai a che fare con un FooManager, sai che gestirà i tipi MyClass e che MyClass estende Foo.

Dalla documentazione di Sun 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 controllata.Una volta che il compilatore conosce il tipo di elemento della raccolta, il compilatore può verificare di aver utilizzato la raccolta in modo coerente e inserire i cast corretti sui valori estratti dalla raccolta...Il codice che utilizza i generici è più chiaro e sicuro.... il compilatore può verificare in fase di compilazione che i vincoli di tipo non vengano violati in fase di esecuzione [sottolineatura 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 farmaci generici, soprattutto nei programmi di grandi dimensioni, è migliore leggibilità e robustezza.[sottolineatura mia]"

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

Usando l'elenco generico puoi avere un Elenco Elenco Elenco quello che desideri e puoi sempre fare riferimento alla digitazione forte, non devi convertire o qualcosa come faresti con un Array o un Elenco standard.

I generici ti consentono di utilizzare una tipizzazione forte per oggetti e strutture dati che dovrebbero essere in grado di contenere qualsiasi oggetto.Elimina inoltre i typecast noiosi e costosi durante il recupero di oggetti da strutture generiche (boxing/unboxing).

Un esempio che utilizza entrambi è un elenco collegato.A cosa servirebbe una classe di elenco collegato se potesse utilizzare solo l'oggetto Foo?Per implementare una lista concatenata in grado di gestire qualsiasi tipo di oggetto, la lista concatenata e i nodi in un'ipotetica classe interna del nodo devono essere generici se si desidera che la lista contenga solo un tipo di oggetto.

Se la tua raccolta contiene tipi di valore, non è necessario eseguire il box/unbox sugli oggetti quando vengono inseriti nella raccolta, quindi le prestazioni aumentano notevolmente.Componenti aggiuntivi interessanti come resharper possono generare più codice per te, come i cicli foreach.

Un altro vantaggio dell'utilizzo dei generici (in particolare con raccolte/elenchi) è che si ottiene il controllo del tipo in fase di compilazione.Ciò è davvero utile quando si utilizza un elenco generico anziché un elenco di oggetti.

La ragione principale è che forniscono Digitare sicurezza

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

al contrario di,

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

come semplice esempio.

In sintesi, i generici ti consentono di specificare con maggiore precisione 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 molti casting di tipo perché sa già che il tipo sarà compatibile.

  • Ciò ti consente anche di ottenere un feedback anticipato sulla correttezza del tuo programma.Cose che in precedenza avrebbero fallito 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 reparto test invii una segnalazione di bug criptico.

  • Il compilatore può eseguire ulteriori ottimizzazioni, come evitare il boxing, ecc.

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

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

Argomenti generici sui metodi possono fare la stessa cosa, ma aiutano anche ad applicare il principio "Dillo, non chiedere" al casting, ad es."dammi quello che voglio e se non puoi dimmi perché".

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
                }
        }));
    }
  .
  .
  .
}

Utilizzando i generici le mie implementazioni di questi DAO costringono lo sviluppatore a passare loro solo le entità per cui sono progettate semplicemente sottoclassando 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 (ha cose come filtraggio, caricamento lento, ricerca).Ho semplicemente semplificato qui per darti un esempio

Io, come te e Steve, abbiamo detto all'inizio "Troppo disordinato e complicato" ma ora ne vedo i vantaggi

Vantaggi evidenti come "sicurezza del tipo" e "nessun casting" sono già menzionati, quindi forse posso parlare di altri "vantaggi" che spero possano essere d'aiuto.

Prima di tutto, i generici sono un concetto indipendente dal linguaggio e, IMO, potrebbe avere più senso se si pensasse contemporaneamente al polimorfismo regolare (di runtime).

Ad esempio, il polimorfismo che conosciamo dalla progettazione orientata agli oggetti ha una nozione di runtime in cui l'oggetto chiamante viene individuato in fase di esecuzione durante l'esecuzione del programma e il metodo pertinente viene chiamato di conseguenza a seconda del tipo di runtime.Nei generici, l'idea è in qualche modo simile, ma tutto avviene in fase di compilazione.Cosa significa e come lo usi?

(Atteniamoci ai metodi generici per mantenerlo compatto) Significa che puoi ancora avere lo stesso metodo su classi separate (come hai fatto in precedenza nelle classi polimorfiche) ma questa volta vengono generati automaticamente dal compilatore in base ai tipi impostati in fase di compilazione.Parametri i tuoi metodi in base al tipo che fornisci in fase di compilazione.Quindi, invece di scrivere i metodi da zero per ogni singola tipologia hai come nel polimorfismo di runtime (sovrascrittura del metodo), lasci che i compilatori facciano il lavoro durante la compilazione.Ciò presenta un ovvio vantaggio poiché non è necessario dedurre tutti i possibili tipi che potrebbero essere utilizzati nel proprio sistema, il che lo rende molto più scalabile senza modificare il 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 utilizzare tipi "limitati" e limitare ciò che può essere passato come tipo parametrizzato attraverso classi/metodi.Quindi, puoi controllare cosa attraversare, il che è una cosa potente soprattutto se hai una struttura che viene consumata da altre persone.

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

Nessuno può impostare qualcosa di diverso da MyObject ora.

Inoltre, puoi "applicare" vincoli di tipo sugli argomenti del tuo metodo, il che significa che puoi assicurarti che entrambi gli argomenti del tuo metodo dipendano dallo stesso tipo.

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

Spero che tutto questo abbia senso.

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

Usare i generici per le collezioni è semplice e pulito.Anche se lo scommetti ovunque, il guadagno dalle collezioni per me è una vittoria.

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

contro

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 farmaci generici e non devi essere un Guru generico per usarlo e ottenere valore.

I generici ti danno anche la possibilità di creare oggetti/metodi più riutilizzabili pur fornendo supporto specifico per il tipo.In alcuni casi ottieni anche molte prestazioni.Non conosco le specifiche complete sui generici Java, ma in .NET posso specificare i vincoli sul parametro Type, come Implements a Interface, Constructor e Derivation.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top