我有一个(通用)类,用于保存其他类的元数据。元数据以多种方式使用(编写和读取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()
。
关于如何解决这个问题的任何想法?
答案 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) {
}
}
如果您不想创建另一种方法,可以使用复合。关于组合而不是继承,有很多很好的讨论。
除了A
和B
类之外,所有内容都是相同的:
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) {
}
}