2011-01-24 14 views
5

Voglio codificare un singolo metodo add() in Java, che potrebbe aggiungere sia interi, stringhe, ecc. Generics mi aiuterà.Perché Generics in Java? Com'è diverso dal sovraccarico?

Non riuscivo a capire l'obiettivo finale di Generics. Sono così confuso.

Generics vs Overloading?

public Integer add(Integer i, Integer j){return i+j;} 
public String add(String i, String j){return i+j;} 
public <T> T add(T i, T j){return i+j;} //this gives me error. 

Per favore fatemi uscire.

Grazie.

+1

Quando si esegue 'Integer i + Integer j', in realtà non si aggiungono' Numeri interi '. 'Interi' non supportano l'operatore '+'. Ricevono unboxed in 'ints', non direttamente aggiunti come oggetti' Integer'. Non è possibile aggiungere oggetti generici perché gli oggetti non supportano l'operatore '+'. 'Stringhe 'sono un caso speciale e supportano' + '. –

+4

L'unico motivo per cui chiamereste entrambi i vostri esempi 'add' è perché usano gli operatori' + ', che in realtà è una similarità senza significato poiché fa cose completamente diverse per Stringhe e Interi. Assegnare nomi a metodi basati su ciò che effettivamente fanno dal punto di vista del chiamante, ad esempio 'sum' e' concatenate' in questo caso sarebbero dei buoni nomi. –

risposta

4

Voglio codificare un singolo metodo add() in Java, che potrebbe aggiungere sia interi, stringhe ecc. Generics mi aiuterà.

NO

generici sono utilizzati per tipo sicurezza soprattutto in fase di compilazione.

È possibile specificare un solo tipo di aggiungere nella collezione

List<String> lst;

accetterà String solo, nessuna superclasse di String nessuna classe sub [per esempio se esiste]

+1

'accetta solo String, nessuna superclasse di String no sub class' - Non è vero. Qualcosa come 'void add42 (Lista elenco) {list.add (42);}' è perfettamente valido. – maaartinus

5

Generics potrebbe aiuto, il problema qui è che l'operazione + è definita solo per le primitive java e String ma non per i tipi in generale. E in Java non possiamo sovraccaricare gli operatori (come possiamo fare in C++ per esempio).

Una soluzione pratica senza farmaci generici sarebbe:

public Integer add(Integer a, Integer b) { return a + b; } // sum of a and b 
public String add(String a, String b) { return a + b; }  // concatenate operation! 
public MyType add(MyType a, MyType b) { return a.add(b); } // requires add operation 
+2

Il tuo secondo paragrafo non è vero. Il tipo restituito non fa parte della firma del metodo in Java, ma solo il nome e i tipi di parametro. Finché i parametri sono distinti, è possibile avere diversi tipi di ritorno. O ho frainteso ciò che hai scritto? –

+0

Il sovraccarico non è determinato con il tipo di reso. Non è necessario che il tipo di reso sia lo stesso per il sovraccarico. – Manoj

+0

@ Jonathan - grazie mille - è stato un tipico ShortOnCoffeeError :-) Modificata la risposta e prenotato un altro corso "Java per principianti";) –

2

generici sono bello perché se fatto correttamente ti permette di generalizzare parte del codice che hai scritto. In termini di software di scrittura, si desidera ridurre al minimo il codice non necessario e riutilizzare il maggior numero possibile di codice.

Utilizzando l'approccio di sovraccarico è necessario scrivere ciascuno dei sovraccarichi, mentre con i generici è necessario farlo solo una volta. I generici funzionano bene in situazioni in cui si sta manipolando una raccolta poiché si eseguono determinate operazioni comuni su una serie di raccolte. In alcune situazioni, se si desidera un approccio più personalizzato alla gestione di alcune situazioni, è possibile che si debbano sovraccaricare i metodi, ma farei l'obiettivo di scrivere generici se è possibile utilizzarlo in più punti.

+0

Quindi mi sono confuso con Java Generics con i modelli C++. Se qualche link per il confronto tra generici e modelli è più utile per me. – Manoj

+1

http://stackoverflow.com/questions/36347/what-are-the-differences-between-generic-types-in-c-and-java Può aiutarti a capire le somiglianze e le differenze tra questi due. –

1

Questo è esattamente il punto di generici. Non si vuole implementare un metodo che possa accettare tutto. Francamente è sempre possibile implementare il metodo

add(Object obj)

e passare ad esso interi, stringhe, booleani ... Ma nella maggior parte dei casi non si vuole a questo.Il metodo deve trattare l'argomento e quindi dovrebbe conoscere il tipo del parametro. Questa è la ragione per cui, se si desidera effettuare metodo Add che riceve le stringhe e interi implementare 2 metodi:

add(String s); 
add(int i); 

Ora è possibile di non inviare booleano aggiungere (metodo): tale metodo semplicemente non esiste.

è anche possibile implementare metodo generico

<T> void add(T arg);

e chiamarlo: this.<String>add("hello"); o this.<Integer>add(123);

In questo caso this.<String>add(123); causa errore di compilazione.

3

Anche se i metodi sembrano quasi identici, in questo caso i generici non possono aiutare. Considerate questo:

public <T> T add(T i, T j){return i+j;} 
... 
add(new Object(), new Object()); 

Che cosa significa per aggiungere al proprio normale Object s insieme? No, + non è supportato per Object e non è supportato per molti altri tipi che potrebbero essere sostituiti per T.

Nella creazione di un metodo generico <T> T add(T i, T j), il metodo generico è limitato ai metodi e alle operazioni che sono ammessi al parametro di tipo generico T, il che significa che in modo efficace solo le cose che si possono fare con Object. Stai tentando di utilizzare un metodo/operazione +, che è non qualcosa che puoi fare con Object.

Potremmo provare a risolvere questo problema trovando una classe base comune di String e Integer che supporta tutti i metodi e le operazioni che dobbiamo utilizzare nel nostro corpo del metodo generico. Se ci fosse una classe base comune XXXXX che ha sostenuto l'operatore +, potremmo fare questo:

public <T extends XXXXX> T add(T i, T j) { return i+j; } 

Tuttavia, non v'è nessuna classe base comune XXXXX che sostiene tutto ciò che vogliamo fare in il metodo generico (+), quindi i generici non possono essere utilizzati per risolvere questo problema.

+1

Grazie per aver menzionato "extends", stavo per farlo io stesso. –

1

Come su un approccio con un'interfaccia generica:

interface Adder<T> { 
    T add(T first, T second); 
} 

e una classe dirigente:

public class AdderManager{ 

    public <T> AdderManager putAdder(final Class<T> clazz, 
     final Adder<? extends T> adder){ 
     adders.put(clazz, adder); 
     return this; 
    } 

    @SuppressWarnings("unchecked") 
    public <T> Adder<T> getAdder(final Class<T> clazz){ 
     return (Adder<T>) adders.get(clazz); 
    } 

    private final Map<Class<?>, Adder<?>> adders = 
     new HashMap<Class<?>, Adder<?>>(); 

} 

Ora è possibile registrare e utilizzare vipere personalizzati per le diverse classi:

final AdderManager manager = new AdderManager(); 
manager 
.putAdder(String.class, new Adder<String>(){ 

    @Override 
    public String add(final String first, final String second){ 
     return first.concat(second); 
    } 
}) 
.putAdder(Integer.class, new Adder<Integer>(){ 

    @Override 
    public Integer add(final Integer first, final Integer second){ 
     return first + second; 
    } 
}) 
.putAdder(List.class, new Adder<List<?>>(){ 

    @Override 
    public List<?> add(final List<?> first, final List<?> second){ 
     final List<Object> newList = new ArrayList<Object>(); 

     return newList; 
     } 
}); 

E ora puoi usare quei sommatori come questo:

String addedString = manager.getAdder(String.class).add("abc", "def"); 

@SuppressWarnings("unchecked") // this is necessary because of 
           // the generic <Integer> type 
List<Integer> addedList = manager 
          .getAdder(List.class) 
          .add(
           Arrays.asList(1,2,3), 
           Arrays.asList(4,5,6) 
          ); 
1

I generici in java aggiungono solo il casting al bytecode.

Il codice sarà lo stesso, ad eccezione del casting.

Così le due costruzioni:

List list1 = new ArrayList(); 
List<String list2 = new ArrayList<String>(); 
.... 
String string1 = (String) list1.get(0); 
String string2 = list2.get(0); 

stanno facendo lo stesso.

5

Altri hanno sottolineato perché questo non funziona. Lasciatemi aggiungere, penso che tu stia fraintendendo a cosa servono i generici Java. Forse hai familiarità con i modelli C++, o con l'espansione della macro C, che somigliano entrambi ai generici ma sono una cosa molto diversa.

I generici sono davvero sulla sicurezza del tipo ed evitano i casini disordinati. Si dice al compilatore che una certa raccolta contiene stringhe e quindi sa che tutto ciò che si inserisce o si estrae deve essere una stringa. Si ottiene un controllo in fase di compilazione delle cose che si inseriscono e si salvano alcuni cast sulle cose che si estraggono.

NON fa in modo che il compilatore generi codice diverso a seconda del tipo. I generici non sono un modo per evitare di dover scrivere, ad esempio, una versione "int" e una versione "float" della stessa funzione, come farebbe una macro o un modello C++. Potresti essere in grado di ottenere un comportamento distinto tanto desiderato avendo due classi che implementano la stessa funzione, ma una che usa int e l'altra che usa float. Ma puoi farlo con tecniche orientate agli oggetti senza generici. Se non riesci a ottenere il comportamento distinto desiderato usando tecniche orientate agli oggetti "non generiche", allora non puoi farlo anche con i generici.