Mi capita spesso trovo a voler scrivere definizioni di classi generiche di formaEvitare tipi generici di forma Foo <ActualType estende Foo <ActualType>>
public class Foo<ActualType extends Foo<ActualType>>
Ad esempio, in una configurazione come questa:
public interface ChangeHandler<SourceType> {
public void onChange(SourceType source);
}
public class Foo<ActualType extends Foo<ActualType>> {
private final List<ChangeHandler<ActualType>> handlers = new ArrayList<>();
public void addChangeHandler(ChangeHandler<ActualType> handler) {
handlers.add(handler);
}
@SuppressWarnings("unchecked")
protected void reportChange() {
for (ChangeHandler<ActualType> handler: handlers)
handler.onChange((ActualType) this);
}
}
public class Bar extends Foo<Bar> {
// things happen in here that call super.reportChange();
}
public static void main(String[] args) throws IOException {
Bar bar = new Bar();
bar.addChangeHandler(new ChangeHandler<Bar>() {
@Override
public void onChange(Bar source) {
// Do something with the changed object
}
});
}
L'evento di cambiamento qui è solo un esempio. Questo è più di un problema generale che sto riscontrando ogni volta che vorrei consentire a una super classe di fornire funzionalità che siano "individualizzate" per ogni sottoclasse specifica (non so come esprimerla meglio ... nell'esempio sopra "l'individualizzazione" è il fatto che il ChangeHandler
viene chiamato con un oggetto del sottotipo effettivo (Bar
) non con il tipo di super-classe (Foo
) che chiama il gestore).
In qualche modo questo approccio mi sembra un po 'complicato. E permette in realtà per i potenziali problemi dal momento che nulla mi impedisce di definire poi:
public class Baz extends Foo<Bar> { /* ... */ }
Esiste un'alternativa più pulita?
il Santo Graal sarebbe un certo parametro di tipo che viene sempre definita per contenere la classe corrente, come una versione statica di this.getClass()
che mi avrebbe permesso di scrivere qualcosa di simile a questo, invece:
public class Foo {
private final List<ChangeHandler<this.Class>> handlers = new ArrayList<>();
public void addChangeHandler(ChangeHandler<this.Class> handler) {
handlers.add(handler);
}
protected void reportChange() {
for (ChangeHandler<this.Class> handler: handlers)
handler.onChange(this);
}
}
Dove this.Class
sarebbe essere uguale a Bar
per le classi di tipo Bar
.
Questa è la versione di Java del [CRTP] (https://stackoverflow.com/questions/4173254/what-is-the-curiously-recurring-template-pattern-crtp). È piuttosto tipico. – dhke
Il Santo Graal sarebbe bello ma non esiste (ancora). Sembra difficile da controllare staticamente a causa dell'ereditarietà. – Radiodef
Non potresti fare 'Foo>', sostituendo tutte le occorrenze di 'ActualType' con' T'? Rimuovi la necessità di 'ActualType'. Non sono sicuro se questo è ciò che intendevi per "più pulito" –