Java未选中覆盖返回类型

我有一个包含以下组件的项目:

public abstract class BaseThing { public abstract  ThingDoer getThingDoer(); } public class SomeThing extends BaseThing { public ThingDoer getThingDoer() { return Things.getSomeThingDoer(); } } public class SomeOtherThing extends BaseThing { public ThingDoer getThingDoer() { return Things.getSomeOtherThingDoer(); } } public class Things { public ThingDoer getSomeThingDoer { return getThingDoer(SomeThing.class); } public ThingDoer getSomeOtherThingDoer { return getThingDoer(SomeOtherThing.class); } private <D extends ThingDoer D getThingDoer(Class clazz) { //get ThingDoer } } public class ThingDoer { public void do(T thing) { //do thing } } public class DoThing { private BaseThing thing; public void doIt() { thing.getThingDoer().do(thing); } } 

我在SomeThing.getThingDoer()中收到编译器警告:

未选中覆盖:返回类型需要未经检查的转换。

找到ThingDoer ,需要ThingDoer

Everthing编译得很好,虽然我还没有机会测试DoThing.doIt()但我没有理由相信它不会起作用。

我的问题是,这可以打破并有更好的方法吗? 我可以将DoThing作为基类,并为SomeThingSomeOtherThing设置子类,但这看起来并不优雅。

编辑:我想避免使BaseThing通用。

让我们首先看一下你不想制作generics的BaseThing类:

 public abstract class BaseThing { public abstract  ThingDoer getThingDoer(); } 

不是generics类,但它包含generics方法 。 通常,这样的generics方法被设计为使得类型基于该方法的某个参数由编译器绑定。 例如: public Class classOf(T object) 。 但在你的情况下,你的方法不带参数。 如果方法的实现从Collections实用程序类返回类似于此方法的“通用”generics(我的术语),那么这也有点常见: public List emptyList() 。 此方法不带参数,但类型将从调用上下文中推断出来; 它的工作原理只是因为emptyList()的实现返回了一个在所有情况下都是类型安全的对象。 由于类型擦除 ,该方法在调用时实际上并不知道T的类型。

现在,回到你的课堂。 在创建BaseThing这些子类时:

 public class SomeThing extends BaseThing { public ThingDoer getThingDoer() { return Things.getSomeThingDoer(); } } public class SomeOtherThing extends BaseThing { public ThingDoer getThingDoer() { return Things.getSomeOtherThingDoer(); } } 

在这里,您希望覆盖基类中的abstract方法。 只要返回类型在原始方法的上下文中仍然有效, Java中就允许覆盖返回类型。 例如,您可以覆盖一个返回Number的方法,该方法具有始终为该方法返回Integer的特定实现,因为Integer 是一个 Number

但是,对于generics, List 不是 List 。 因此,虽然您的抽象方法被定义为返回ThingDoer (对于某些T extends BaseThing ),但是返回ThingDoerThingDoer通常不会与某些未知的T兼容,即使SomeThingSomeOtherThing都从BaseThing扩展。

调用者(来自抽象API)期望一些未知的,无法执行的T无法保证满足您的任何具体实现。 实际上,您的具体重载不再是通用的(它们返回特定的,静态绑定的类型参数)并且与抽象类中的定义冲突。

编辑 :定义抽象方法的“正确”方式(无警告)应该是这样的:

 public abstract ThingDoer getThingDoer(); 

这使得调用者明白它正在获取ThingDoer ,其第一个类型参数绑定到扩展BaseThing 东西 (因此它可以像使用BaseThing使用它)但是调用者在抽象访问时不会知道具体的实现API。

编辑#2 – 我们在聊天中讨论的结果……

OP的原始示例用法是:

 BaseThing thing = /* ... */; thing.getThingDoer().do(thing); 

注意同一个thing引用是如何传递回从同一个东西的getThingDoer()方法返回的对象中的方法。 getThingDoer()返回的对象需要紧密绑定到具体的实现类型(根据OP)。 对我来说,这闻起来像破碎的封装。

相反,我建议将逻辑操作公开为BaseThing API的一部分,并将ThingDoer作为内部实现细节封装到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); } }