views:

512

answers:

4

I want to have a class which implements an interface, which specifies the specific subclass as a parameter.

public abstract Task implements TaskStatus<Task> {
  TaskStatus<T> listener;

  protected complete() {
      // ugly, unsafe cast
      callback.complete((T) this);
  }
}

public interface TaskStatus<T> {
   public void complete(T task);
}

But instead of just task, or , I want to guarantee the type-arg used is that of the specific class extending this one.

So the best I've come up with is:

public abstract Task<T extends Task> implements TaskStatus<T> {
}

You'd extend that by writing:

public class MyTask extends Task<MyTask> {
}

But this would also be valid:

public class MyTask extends Task<SomeOtherTask> {
}

And the invocation of callback will blow up with ClassCastException. So, is this approach just wrong and broken, or is there a right way to do this I've somehow missed?

+2  A: 

It is not clear what you are trying to do inside of Task. However, if you define the generic class Task<T> as follows:

class Task<T extends Task<T>> { ... }

The following two are possible:

class MyTask extends Task<MyTask> { ... }
class YourTask extends Task<MyTask> { ... }

But the following is prohibited:

class MyTask extends Task<String> { ... }

The above definition of Task uses F-bounded polymorphism, a rather advanced feature. You can check the research paper "F-bounded polymorphism for object-oriented programming" for more information.

Bruno De Fraine
A: 

I'm having a little trouble understanding what you're trying to accomplish through this. Could you provide some more details?

My read of the code says you have these tasks which will be sub-classed and after the tasks are complete, the executing thread will call complete() on the task. At this point you want to call a callback and pass it the sub-class object. I think this is the problem. You are trying to put knowledge of the potential sub-classes into your abstract class which is a no-no.

This also raises the question of, if you could make this call, what is the callback going to do with the sub-class that would be different from the superclass?

David Smith
+1  A: 

I suggest adding a getThis which should return this appropriately typed. Sure a subclas could misbehave, but that's always true. What you avoid is the cast and the possibility of a ClassCastException.

public abstract class Task<THIS extends Task<THIS>> {
    private TaskStatus<THIS> callback;

    public void setCallback(TaskStatus<THIS> callback) {
        this.callback = callback==null ? NullCallback.INSTANCE : callback;
    }

    protected void complete() {
        // ugly, unsafe cast
        callback.complete(getThis());
    }

    protected abstract THIS getThis();
}

public interface TaskStatus<T/* extends Task<T>*/> {
    void complete(T task);
}

public class MyTask extends Task<MyTask> {
    @Override protected MyTask getThis() {
        return this;
    }
}

This problem often comes up with builders.

Tom Hawtin - tackline
A: 

I can really only see this working if you in the constructor enforce the type arg. Via. introspection and the Class.getSuperType() you can inspect the type args, and verify that the type arg matches this class.

Somthing along the lines of:

assert getClass() == ((ParameterizedType) getSuperType()).getTypeArguments()[0];

(This is from the top off my head, check JavaDocs to verify).

I am not sure where callback is created though in your code. You have omitted it's declaration in the top.

A different route would be to remove the unsafe cast, as atm. I can't see the full flow to spot why you need the unsafe cast.

Staale