泛型,超级和扩展的问题

时间:2013-07-25 08:11:24

标签: java generics inheritance

我有一个(通用)类,用于保存其他类的元数据。元数据以多种方式使用(编写和读取XML数据,数据库,输出文本等)。到目前为止这个工作。但是,当从其他类继承的类中使用所有这些时,我遇到了一个问题。

请看下面的代码(我试图生成一个最小的例子,除了下面标出的行之外是compilabe):

class A {
  public Meta<? extends A> getMeta() {
    return new Meta<A>();
  }

  public void output() {
    /*
     * Error shown in eclipse for the next line:
     * The method output(capture#1-of ? extends A) in the type
     * Outputter<capture#1-of ? extends A> is not applicable for the arguments
     * (A)
     */
    getMeta().getOutputter().output(this);
  }
}

class B extends A {
  @Override
  public Meta<? extends B> getMeta() {
    return new Meta<B>();
  }
}

class Meta<CLS> {
  public Outputter<CLS> getOutputter() {
    return null;
  }
}

class Outputter<CLS> {
  public void output(CLS obj) {
  }
}

我可以更改A.getMeta()以返回Meta<A>以进行上线编辑,但之后我无法将其覆盖为B类中的Meta<B> getMeta()

关于如何解决这个问题的任何想法?

3 个答案:

答案 0 :(得分:1)

如果你这样做怎么办?它需要一个更多的课程,但似乎它会起作用:

class T{
 //put common methods here, generic methods are not common, so they will not be here
}

 class A extends T{
  public Meta<A> getMeta() {
    return new Meta<A>();
  }

  public void output() {
    /*
     * Error shown in eclipse for the next line:
     * The method output(capture#1-of ? extends A) in the type
     * Outputter<capture#1-of ? extends A> is not applicable for the arguments
     * (A)
     */
    getMeta().getOutputter().output(this);
  }
}

class B extends T {

  public Meta<B> getMeta() {
    return new Meta<B>();
  }
}

class Meta<CLS> {
  public Outputter<CLS> getOutputter() {
    return null;
  }
}

class Outputter<CLS> {
  public void output(CLS obj) {
  }
}

如果您不想创建另一种方法,可以使用复合。关于组合而不是继承,有很多很好的讨论。

除了AB类之外,所有内容都是相同的:

  class A{
      public Meta<A> getMeta() {
        return new Meta<A>();
      }
      ...
     } 

    class B {

      private class A a; 

      public Meta<B> getMeta() {
        return new Meta<B>();
      }
      //use a here if you need, a is composed into B
    }

答案 1 :(得分:1)

您无法使用public Meta<A> getMeta()覆盖public Meta<B> getMeta()的原因是B的实例可以转换为A,并且此类转换实例需要返回Meta<A>。虽然可能Meta<B>可以作为Meta<A>,但编译器不知道这一点。

想象一下,您正在返回List<A>List<B>。允许将A和B的实例放入List<B>,但不允许将B的实例放入List<A>,因此实际由B返回的List<B>不能作为List<A>

List<A>更改为List<? extends A>允许编译代码,因为List<B>在技术上是List<? extends A>的子类,但它不允许您执行您可能期望的所有操作

B b = new B();
A casted = (A)b;
casted.getList().add(new A());

编译器会毫无问题地接受第一行和第二行,但第三行会遇到问题:

The method add(capture#1-of ? extends A) in the type List<capture#1-of ? extends A> is not applicable for the arguments (A)

如果你稍微调查一下,你会发现这个转换变量既不接受A也不接受B的元素。编译器已经记住该对象已被转换,实际上可能无法接受任何扩展A的内容。

我正在尝试搜索此行为的文档,但我失败了。 Eclipse工具提示建议我应该给它一个null类型的元素,这显然是无稽之谈。如果我发现任何内容,我会更新。


编辑:所描述的行为是“捕获转化”的产物,如here所述。通过在赋值和强制转换过程中更改类型参数的边界,Capture Conversion允许通配符更有用。在我们的代码中发生的只是边界被限制在null类型。

答案 2 :(得分:0)

自从我找到一个有效的解决方案后,我会自己回答。

虽然此解决方案不是类型安全的,但它可以正常工作,并且对现有代码库的更改要求最少。如果有人提出有效且不需要@SuppressWarnings的内容,我会接受这个答案。

class A {
  Meta<?> getMeta() {
    return new Meta<A>();
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  public void output() {
    Outputter out = getMeta().getOutputter();
    out.output(this);
  }
}

class B extends A {
  @Override
  public Meta<?> getMeta() {
    return new Meta<B>();
  }
}

class Meta<CLS> {
  public Outputter<CLS> getOutputter() {
    return null;
  }
}

class Outputter<CLS> {
  public void output(CLS obj) {
  }
}