I frequently find myself wanting to write generic class definitions of the form
public class Foo<ActualType extends Foo<ActualType>>
For example in a setup like this:
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
}
});
}
The change-event here is just an example. This is more of a general problem that I'm having whenever I would like to allow a super-class to provide functionality that is "individualized" to each specific sub-class (not sure how to phrase this better... in the example above the "individualization" is the fact that the ChangeHandler
is called with an object of the actual sub-type (Bar
) not with the type of the super-class (Foo
) that is calling the handler).
Somehow this approach seems a bit messy to me. And it actually allows for potential issues since nothing prevents me from then defining:
public class Baz extends Foo<Bar> { /* ... */ }
Is there a cleaner alternative?
The holy grail would be some type parameter that is always defined to contain the current class, like a static version of this.getClass()
that would allow me to write something like this instead:
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);
}
}
Where this.Class
would be equal to Bar
for classes of type Bar
.