指定扩展类的泛型类型args?

时间:2008-09-30 17:01:16

标签: java generics design-patterns

我希望有一个实现接口的类,它将特定的子类指定为参数。

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);
}

但不是仅仅是任务,或者,我想保证使用的类型arg是扩展此类的特定类的类型。

所以我想出的最好的是:

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

你可以通过写下来扩展它:

public class MyTask extends Task<MyTask> {
}

但这也是有效的:

public class MyTask extends Task<SomeOtherTask> {
}

并且回调的调用会因ClassCastException而爆炸。那么,这种方法是错误的还是破碎的,还是有正确的方法来做到这一点我不知何故错过了?

4 个答案:

答案 0 :(得分:2)

目前尚不清楚你在Task内想要做什么。但是,如果您按如下方式定义泛型类Task<T>

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

以下两种是可能的:

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

但禁止以下内容:

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

Task的上述定义使用F-bounded多态,这是一个相当高级的特性。您可以查看研究论文“F-bounded polymorphism for object-oriented programming”以获取更多信息。

答案 1 :(得分:1)

我建议添加一个getThis,它应该返回适​​当的类型。当然,一个子类可能行为不端,但这总是如此。你避免的是演员阵容和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;
    }
}

这个问题经常出现在构建者身上。

答案 2 :(得分:0)

我很难理解你要通过这个来完成什么。你能提供更多细节吗?

我对代码的阅读说明你有这些任务将被分类,并且在任务完成后,执行线程将在任务上调用complete()。此时,您需要调用回调并将其传递给子类对象。我认为这是问题所在。您正试图将潜在子类的知识放入您的抽象类中,这是一个禁忌。

这也提出了一个问题,如果你可以进行这个调用,那么与超类不同的子类的回调是什么?

答案 3 :(得分:0)

如果你在构造函数中强制执行arg类型,我真的只能看到这个工作。通过。内省和Class.getSuperType()可以检查类型args,并验证类型arg是否与此类匹配。

沿着以下方向做的事:

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

(这是从头到尾,检查JavaDocs验证)。

我不确定在代码中创建回调的位置。你在顶部省略了它的声明。

另一条路线是移除不安全的演员阵容,如atm。我无法看到完整的流程来发现你为何需要不安全的演员。