2011-01-11 3 views
5

Qualcuno può spiegare l'utilizzo di template generico ricorsivo piuttosto complesso di seguito?Modello generico ricorsivo Java: cosa significa questo per ... S extends Writer <E>> extends Entity <E,S>

public abstract class Data<E extends Data<E, S>, 
          S extends Writer<E>> extends Entity<E,S> 

Cosa dovremmo tenere a mente durante l'utilizzo di generici ricorsivi, come sopra. E come sarà la relazione e le regole tra questi tipi, qui E & S?

Se uno qualsiasi, si prega di fornire alcune risorse/link/libri su questo tipo di utilizzo generico. So che un libro a parlare di questo, Effective Java, 2a ed da Joshua Bloch (Articolo 27)

+0

Se fossi un boss mi respingere colui che ha scritto questo codice. Che diavolo è quello ? Uno dovrebbe capire qualcosa a prima vista –

risposta

2

Data ha due parametri, E che deve in definitiva essere un'istanza di se stesso, e S che deve essere in grado di Writer un'istanza di per sé (in particolare, lo stesso tipo di istanza di se stesso specificato da E). Infine, Data<E,S> qualifica anche come/eredita capacità dalle Entity parametrizzate dalla stessa E e S (cioè, Entity è di Data<E,S> e Writer<E>).

Una concreta attuazione potrebbe essere simile

NumericalData extends Data<NumericalData, NumWriter> dove NumWriter attrezzi/estende verso Writer<NumericalData> e NumericalData qualifica anche come un Entity<NumericalData, NumWriter>.

MODIFICA:

Perché fare qualcosa del genere? Si potrebbe voler definire metodi generici nella classe astratta che si basano su un argomento/ritorno che soddisfano i criteri Data<E,S>, ma vogliono anche essere in grado di restituire/lavorare con il tipo più esplicito. Per esempio, in Data<E,S>, ci potrebbe essere

E doSomething(E toThis) { toThis.aDataClassMethod(); return toThis; } 

La classe può fare la prima chiamata, perché sa E è un Data<E,S>, e restituire il tipo più specifico perché sa toThis è un E.

Per essere onesti, i generici ricorsivi sono tipicamente la strada troppo intelligente. Possono essere utili, ma molte volte sono semplicemente "ordinate" e si cerca di piegare il problema su qualcosa di intelligente piuttosto che viceversa.

3

Iniziamo con il più facile

S extends Writer<E> 

Ogni classe di tipo S deve essere uno scrittore per la classe E

extends Entity<E,S> 

Proprio eredità qui, la classe di dati estende la classe Entity.

E extends Data<E, S> 

Qualsiasi classe utilizzata per E deve sé ereditare dalla classe di dati ed eredita/implementa i metodi generici di dati utilizzando il proprio tipo e uno scrittore compatibile con sé.

Il rapporto tra E & S dovrebbe essere qualcosa di simile al seguente:

//E = Example, S = ExampleWriter 
public class ExampleWriter implements Writer<Example>{ 
//... 
} 
public class Example extends Data<Example,ExampleWriter>{ 
//... 
} 

da tenere a mente: con i generici fornire un Writer<SomeChildOfExample> o potrebbe Writer<SomeParentOfExample> o non potrebbe creare errori di compilazione, questo dipende dalla generica metodi definiti in entrambi i tipi generici.

+0

L'esempio che hai specificato per descrivere la relazione sembra buono. – manikanta

0

Sono d'accordo con Carl che i tipi ricorsivi tendono ad essere "intelligenti" a scapito dell'usabilità. Tuttavia ci sono MOLTI casi in cui Java rtl avrebbe dovuto usare questo idioma per imporre una rigorosa sicurezza di tipo ed evitare il barile di scimmie che abbiamo come libreria di classi.

Ad esempio, anche oggetto dovrebbe probabilmente essere un tipo ricorsivo astratta almeno di far rispettare regole severe per l'uguaglianza:

public abstract class Object<T extends Object<T>> { 
    ... 
    public boolean equals(o :T) { 
    ... 
    } 
} 

controlli Non più instanceof nelle vostre equals() implementazioni e, soprattutto, meglio controllo in fase di compilazione per le chiamate equals().

Detto questo, forse una caratteristica più adatto e meno complicato sarebbe un tipo di "Self" ...