【问题标题】:Generic type args which specificy the extending class?泛型类型参数,具体扩展类?
【发布时间】:2008-09-30 17:01:15
【问题描述】:

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

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 而爆炸。那么,这种方法是错误的和被破坏的,还是有一种我错过的正确方法?

【问题讨论】:

    标签: java generics design-patterns


    【解决方案1】:

    目前尚不清楚您要在 Task 内部做什么。但是,如果您将泛型类 Task&lt;T&gt; 定义如下:

    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 for object-oriented programming”了解更多信息。

    【讨论】:

      【解决方案2】:

      我建议添加一个 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;
          }
      }
      

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

      【讨论】:

      • 如果你删除了extends This&lt;THIS&gt;,这里的代码仍然可以编译,但我认为这不是没有意义的。它很有用的原因有很多,包括我想不到的:它为读取提供信息,它允许使用 Task&lt;?&gt;,它更好地利用了 JVM 底层静态类型系统,它允许更好的重载,泛型可以在需要Task&lt;&gt; 的地方使用参数。
      【解决方案3】:

      我在理解您想要通过此实现的目标时遇到了一些问题。能否提供更多细节?

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

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

      【讨论】:

        【解决方案4】:

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

        类似于:

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

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

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

        另一种方法是删除不安全的演员表,如 atm.我看不到完整的流程来找出您需要不安全演员表的原因。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-24
          • 1970-01-01
          • 1970-01-01
          • 2016-09-14
          • 2023-02-23
          • 1970-01-01
          相关资源
          最近更新 更多