4

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

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

但不仅仅是任务,或者,我想保证使用的类型参数是扩展这个的特定类的类型。

所以我想出的最好的是:

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

你可以通过写来扩展它:

public class MyTask extends Task<MyTask> {
}

但这也是有效的:

public class MyTask extends Task<SomeOtherTask> {
}

并且回调的调用将因 ClassCastException 而爆炸。那么,这种方法是错误的和被破坏的,还是有一种我错过的正确方法?

4

4 回答 4

3

目前尚不清楚您要在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 有界多态性,这是一个相当高级的特性。您可以查看研究论文“面向对象编程的 F-bounded polymorphism ”以获取更多信息。

于 2008-09-30T18:30:45.267 回答
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;
    }
}

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

于 2008-09-30T18:35:42.370 回答
0

我在理解您要通过此实现的目标时遇到了一些麻烦。你能提供更多细节吗?

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

这也提出了一个问题,如果您可以进行此调用,那么回调将如何处理与超类不同的子类?

于 2008-09-30T18:35:29.407 回答
0

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

类似于以下内容:

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

(这是我的想法,检查 JavaDocs 来验证)。

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

另一种方法是删除不安全的演员表,如 atm。我看不到完整的流程来发现为什么你需要不安全的演员表。

于 2008-09-30T18:36:55.440 回答