12

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.

Markus A.
  • 12,349
  • 8
  • 52
  • 116
  • 1
    This is Java's version of the [CRTP](https://stackoverflow.com/questions/4173254/what-is-the-curiously-recurring-template-pattern-crtp). It's pretty typical. – dhke May 21 '15 at 19:45
  • The holy grail would be nice but it doesn't exist (yet). It seems tough to check statically because of inheritance. – Radiodef May 21 '15 at 19:46
  • Couldn't you do `Foo>`, replacing all occurances of `ActualType` with `T`? Remove the need for `ActualType`. Not sure if that's what you meant by "cleaner" – Vince May 21 '15 at 19:54
  • @Radiodef Maybe one could just implicitly add the `>` construct to every class definition and handle it that way? – Markus A. May 21 '15 at 19:55
  • @VinceEmigh My real issue with the approach is that it isn't as safe as it seems: The generics check can be broken by something as simple as the typo in the definition of `Baz` above which will only be caught at run-time when the cast of `Baz` to `Bar` in `reportChange()` fails. – Markus A. May 21 '15 at 19:57
  • 1
    The situation that's tough to check statically is like this: suppose you have a `class Foo { void m(this.Class in) {} }` and a `class Bar extends Foo {}`. Now suppose you have a `Foo foo = new Bar();`. What type should `foo.m` accept? – Radiodef May 21 '15 at 19:59
  • @Radiodef Gotcha! Technically, it would need to accept `Bar`, but that wouldn't make any sense from the point of view of the person calling the method on `foo`... That's probably the reason why this functionality doesn't exist. Good point. – Markus A. May 21 '15 at 20:02
  • @Radiodef So it looks like I can't really get around this construct... Do you know if there is a way, though, to prevent the illegal definition of `Baz` that I posted above? Also, if you like, you could turn your comment into an answer. It's a great explanation for why one can't do better without creating ambiguities elsewhere. – Markus A. May 21 '15 at 20:06
  • What about doing `handler.onChange(getFoo())`, supplying a ` T getFoo() { return (T) this; }`? The cast triggers an unsafe warning, but I don't see any problems with it, as long as you make `Foo` abstract – Vince May 21 '15 at 20:13
  • @VinceEmigh Actually, the type-casting exception will probably come up in the ChangeHandler that is being called, which will try to make use of a `Baz` object as if it were a `Bar` object. Inside `Foo`, all the types are erased away anyways, so it can't be fixed there. Most likely the programmer will notice the typo when the `addChangeHandler(...)` method refuses to accept a handler of type `ChangeHandler`, but I wouldn't want to rely on it. One of the benefits of generics is that many of these type-checks can be turned into a compile-time check. – Markus A. May 21 '15 at 20:17
  • To me it sounds for what you want to do you could use an Observer or Publish/Subscribe pattern for example. I'm sorry but I don't see the reason why you want to have those kind of generics. E.g. http://en.wikipedia.org/wiki/Observer_pattern – maraca May 22 '15 at 03:11
  • @maraca That's exactly what the above code is, isn't it? `Foo` is the subject and the different `ChangeHandlers` are the observers. The point of the generics is to allow the `Bar` subject to notify the observers in a more useful way by sending them a `Bar` object rather than just a `Foo` object while still allowing the `Foo` class to provide the framework. Note, though, that this change-handler example was really just an illustration of the general problem. – Markus A. May 22 '15 at 04:31
  • @MarkusA. Ok, to go more into details, here are some thoughts: Why don't you use an interface or an abstract class for the handlers? You can incorporate everything that Foo needs to know there. It seems to me like Foo is trying to cast for it's Handlers. Difficult to explain what I mean... e.g. why do you need generics in ChangeHandler, does it really have to know the class, it can be done without like `onChange(SrcSuperClass src)` or `onChange(SrcInterface src)`. – maraca May 22 '15 at 16:54
  • @maraca Sorry... I don't think I'm fully following your idea... Isn't that exactly what I have? The only thing I'm trying to do is pass a `Bar` object to the `onChange(...)` method so that the change handler has access to all of `Bar`'s functionality rather than just being passed a `Foo` (where it says "do something with the changed object" in the code above). Yes, I could just add a type-cast there (`Bar barSource = (Bar) source;`, but then I should also add an `isinstance` check as the compiler doesn't make sure that I will actually be passed a `Bar` object. It's just more messy that way... – Markus A. May 22 '15 at 17:46
  • @MarkusA. Could you give a short example with real objects, it makes it a lot easier: e.g. you have list of different houses (subclasses of house). You can add detectives (implements detective) to a house which whatch the doors and notify the police if someone entered/left the house. The police wants to know the house and detective reporting... and everything is just a model because it's a library... Then I could give real examples. – maraca May 22 '15 at 18:13
  • @maraca Considering the change-event thing above was also just meant as an example and is not a real problem I'm trying to solve, I'm not sure that adding another example to the question will bring much benefit. If you like, why don't you write an answer that demonstrates your suggestion (it seems like you have a specific solution in mind and I doubt I'd be able to write a house/detective-example in a way that it will work towards that any more than my above example already does) and we can use that as a basis for discussion. – Markus A. May 22 '15 at 18:21
  • As humbling as this is for me, and as frustrating it is for me as a "Java fan": This (namely the lack of a possiblity for a type to refer to itself) has occasionally led me to think: "Scr*w this, I'll remove the generics and add a `@SuppressWarnings("unchecked")`". I'd **really** be happy to hear about a better solution. – Marco13 May 22 '15 at 19:17

3 Answers3

1

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?

Community
  • 1
  • 1
ZhongYu
  • 19,446
  • 5
  • 33
  • 61
0

It is a really abstract problem. In my opinion the short answer to "how to make this cleaner" is: only use generics where it is needed.

public class List<T extends List<T>>

What is this trying to express (substituted)? A list which only allows to hold (T extends) other lists which themselves hold Ts (List) which as we know from before are Lists which only allow to hold ... and so on. Kind of circular, I don't see how you would end up with something like that?

public interface ChangeHandler<SourceType> {
    public void onChange(SourceType source);
}

Why do you want to use generics here? If you want to have a change handler which can handle several resource types, then you can either create a super class from which all actual sources inherit or you create an interface which is implemented by the sources. Like that you can exactly specify what is exposed by the sources. Alternatively the source can create a source object when notifying instead of passing "this" (then it is more like a message). For example:

public interface ChangeHandler {
    public void onChange(Source source);
}

public abstract class Source {
    private List<ChangeHandler> handlers;
    protected int nr;
    public Source(int nr) {
      this.nr = nr;
    }
    public abstract Something getSomething();
    public String toString() {
        return "SRC" + nr;
    }
    ...
    private notify(int index) {
        handlers.get(i).onChange(this);
    }
}

public class Foo extends Source {
    public Foo(int nr) {
        super(nr);
    }
    public String toString() {
        return super.toString() + "Foo";
    }
    public Something getSomething() {
        return new Something();
    }
}

You never need to cast... or do you? I'm not sure if I understand the problem.

maraca
  • 8,468
  • 3
  • 23
  • 45
  • Let's say your `Foo` has some attribute (`title`, for example) that `Source` does not have, you would like to be able to write a `ChangeHandler` that is passed a `source` of type `Foo`, so it can do things like `myLabel.setText(source.getTitle());`. In your proposal, `source` (inside `onChange`) would be of type `Source`, i.e. would not have a title. Therefore, you'd need to write things like `myLabel.setText(((Foo) source).getTitle());` everywhere. And technically since you can now pass this `ChangeHandler` to other `Sources` you should first make sure that `source` even is of type `Foo`. – Markus A. May 22 '15 at 19:18
  • @MarkusA. If you want to have the title in the sources then you should have `public abstract String getTitle();` in the Source class. But I guess you are looking at something where not every source has the title and if you need to cast you would like to detect errors already at compile time if possible? – maraca May 22 '15 at 19:35
  • That's exactly right! In fact, in many cases `Source` can be so general that it shouldn't even have to know about its potential sub-classes. Especially if it is part of a library. Take the `Foo` class in my question, for example. You could envision renaming it to `AbstractChangeReportingObject` and sticking it in a library to easily provide change-handler management for any potential sub-classes, whatever they might be. But the change handlers would still like to be passed a source of the type of object that they are monitoring as an `AbstractChangeReportingObject` will be quite useless... – Markus A. May 22 '15 at 20:42
  • @MarkusA. Quite intersting. I guess for a really good answer you need an expert in Java library design. If it is not a library you could have a (abstract) sub class `TitledSource` which overrides (of course can't make it private then) `notify(int index)` and does the cast. You also could extend the interface (`interface TitledChangeHandler extends ChangeHandler` with `onChange(TitledSource src);`) and tighten the method to add a listener, e.g. `addChangeHandler(TitledSource src)` in `TitledSource`... can detect errors at compile time but isn't as flexible. – maraca May 22 '15 at 21:42
0

I never use type parameters to pass "ActualType" because then it is impossible to extend the object:

public class Bar extends Foo<Bar> {
    // things happen in here that call super.reportChange();
}
public class Bar2 extends Bar{
    // ...
}

Bar2 "ActualType" is still Bar, and there is nothing you can do: you won't be able to use ChangeHandlers for Bar2

To avoid the issue, the only possible fix I see is to delegate the cast operation to an other class (you could also use a default method in the ChangeHandler interface). Here is a possibility:

public class Foo // no more type parameter
{
    private final List<FooCaster<?>> casterHandlers = new ArrayList<>();
    
    /**
     * unsafe because you could register a ChangerHandler of any type.
     * worst of all, everything is unchecked cast so the error could show up late.
     */
    public <T> void addChangeHandler(ChangeHandler<T> handler) {
        casterHandlers.add(new FooCaster<T>(handler));
    }

    protected void reportChange() {
        for (FooCaster<?> caster: casterHandlers) {
            caster.reportChange(this);
        }
    }
    
    class FooCaster<T> {
        protected ChangeHandler<T> ch;
        protected FooCaster(ChangeHandler<T> ch) {
            this.ch = ch;
        }
        
        @SuppressWarnings("unchecked")
        public void reportChange(Foo f) {
            ch.onChange((T)f);
        }
    }
}

Personnaly in the case of broadcasting changes to listener/changehandlers, I'm enclined to externalize the process to an other class, which makes it possible to use parameter types properly and avoid unsafe casts.If you are still willing to use reportChange() from within the foo object, here is a possible implementation (otherwise you could store a T reference in the Broadcaster).

public class Broadcaster<T extends Foo> {
    protected final List<ChangeHandler<? super T>> changeHandlers;
    
    public Broadcaster() {
        this.changeHandlers = new ArrayList<>();
    }
    
    public void startListeningTo(T obj) {// only T type objects are accepted
        obj.registerBroadcaster(this);
    }
    
    public void addChangeHandler(ChangeHandler<? super T> changeHandler) {
        changeHandlers.add(changeHandler);
    }
    
    void reportChange(Foo obj) {
        T o = (T)obj;
        for(ChangeHandler<? super T> c : changeHandlers) {
            c.onChange(o);
        }
    }
}

public class Foo {
    private final List<Broadcaster<?>> broadcasters = new ArrayList<>();
    
    // cannot be accessed from outside of the package, only Broadcaster.startListeningTo(T o) can be used
    final void registerBroadcaster(Broadcaster<?> b) {
        broadcasters.add(b);
    }

    public final void reportChange() {
        for (Broadcaster<?> b: broadcasters) {
            b.reportChange(this);
        }
    }
}

public class Bar extends Foo {
    // things happen in here that call super.reportChange();
}

public static void test() {
    Broadcaster<Bar> broadcaster = new Broadcaster<>();
    broadcaster.addChangeHandler(new ChangeHandler<Bar>() {
        @Override
        public void onChange(Bar obj) {
            // do something
        }
    });
    
    //note that you can use the same broadcaster for multiple objects.
    Bar b = new Bar();
    broadcaster.startListeningTo(b);
    b.reportChange();
}

Note that you will not be able to add changeHandlers from within Bar (but is it really the object's job to register changeHandlers for itself?).

Fogux
  • 61
  • 7