我们先看看你的BaseThing
您不想通用的类:
public abstract class BaseThing {
public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer();
}
This is not一个泛型类,但它包含一个通用方法。通常,像这样的泛型方法的设计使得类型<T>
由编译器根据该方法的某些参数进行绑定。例如:public <T> Class<T> classOf(T object)
。但就您而言,您的方法不带任何参数。这也有些常见,在方法的实现返回一些“通用”通用(我的术语)的情况下,就像来自Collections
实用类:public <T> List<T> emptyList()
。该方法不带参数,但类型<T>
将inferred来自调用上下文;有用only因为实施emptyList()
返回一个在所有情况下都是类型安全的对象。由于类型擦除,该方法实际上并不知道T
当它被调用时。
现在,回到你的课堂。当您创建这些子类时BaseThing
:
public class SomeThing extends BaseThing {
public ThingDoer<SomeThing, String> getThingDoer() {
return Things.getSomeThingDoer();
}
}
public class SomeOtherThing extends BaseThing {
public ThingDoer<SomeOtherThing, String> getThingDoer() {
return Things.getSomeOtherThingDoer();
}
}
在这里,您想要覆盖abstract
来自基类的方法。 Java 中允许重写返回类型只要返回类型在原始方法的上下文中仍然有效。例如,您可以重写返回的方法Number
具有始终返回的特定实现Integer
对于该方法,因为Integer
is a Number
.
然而,对于泛型,List<Integer>
is not a List<Number>
。因此,虽然您的抽象方法被定义为返回ThingDoer<T, String>
(对于一些T extends BaseThing
),你的重载返回ThingDoer<SomeThing, String>
and ThingDoer<SomeOtherThing, String>
通常与某些未知的不兼容T
虽然SomeThing
and SomeOtherThing
两者都延伸自BaseThing
.
调用者(来自抽象 API)期望一些未知的、不可执行的T
不能保证您的任何具体实现都能满足这一要求。事实上,您的具体重载不再是通用的(它们返回特定的静态绑定类型参数),并且与抽象类中的定义相冲突。
EDIT:
定义抽象方法的“正确”方法(没有警告)应该是这样的:
public abstract ThingDoer<? extends BaseThing, String> getThingDoer();
这让调用者清楚地知道它正在得到一个ThingDoer
它的第一个类型参数绑定到某物延伸BaseThing
(所以它可以像使用它一样使用它BaseThing
)但是调用者will not通过抽象API访问时知道具体的实现。
EDIT #2- 我们在聊天中讨论的结果...
OP的原始示例用法是:
BaseThing thing = /* ... */;
thing.getThingDoer().do(thing);
注意如何相同thing
引用被传递回从同一事物返回的对象中的方法getThingDoer()
方法。返回的对象getThingDoer()
需要与具体的实现类型紧密结合thing
(根据OP)。对我来说,这闻起来就像封装破裂了。
相反,我建议将逻辑操作公开为BaseThing
API 并将委托封装到ThingDoer
作为内部实施细节。生成的 API 看起来像这样:
thing.doTheThing();
并实现有点像:
public class SomeThing extends BaseThing {
@Override public void doTheThing() {
Things.getSomeThingDoer().do(this);
}
}
public class SomeOtherThing extends BaseThing {
@Override public void doTheThing() {
Things.getSomeOtherThingDoer().do(this);
}
}