Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding generic types of form Foo<ActualType extends Foo<ActualType>>

Tags:

java

generics

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.

like image 604
Markus A. Avatar asked Mar 18 '26 23:03

Markus A.


1 Answers

I would recommend that we simply use <This> to represent the "self type". No need for bound, since it looks complicated, doesn't deliver the intention, and cannot enforce the constraint anyway.

public class Foo<This> {

    private final List<ChangeHandler<This>> handlers = new ArrayList<>();

    public void addChangeHandler(ChangeHandler<This> handler) {
        handlers.add(handler);
    }

    @SuppressWarnings("unchecked")
    protected void reportChange() {
        for (ChangeHandler<This> handler: handlers)
            handler.onChange( (This)this );
    }
}

Notice the cast (This)this.

See also Java generics: Use this type as return type?

like image 120
ZhongYu Avatar answered Mar 20 '26 11:03

ZhongYu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!