Eli*_*zer 8 java generics polymorphism abstract-class return-type
我有一个包含以下组件的项目:
public abstract class BaseThing {
public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer();
}
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();
}
}
public class Things {
public ThingDoer<SomeThing, String> getSomeThingDoer {
return getThingDoer(SomeThing.class);
}
public ThingDoer<SomeOtherThing, String> getSomeOtherThingDoer {
return getThingDoer(SomeOtherThing.class);
}
private <D extends ThingDoer<T, String> D getThingDoer(Class<T> clazz) {
//get ThingDoer
}
}
public class ThingDoer<T, V> {
public void do(T thing) {
//do thing
}
}
public class DoThing {
private BaseThing thing;
public void doIt() {
thing.getThingDoer().do(thing);
}
}
Run Code Online (Sandbox Code Playgroud)
我收到编译器警告SomeThing.getThingDoer()说:
未选中覆盖:返回类型需要未经检查的转换.
发现
ThingDoer<SomeThing, String>,必需ThingDoer<T, String>
Everthing编译得很好,虽然我没有机会测试,DoThing.doIt()但我没有理由相信它不会起作用.
我的问题是,这可以打破并有更好的方法来做到这一点吗?我可以创建DoThing一个基类,并为两者都有子类SomeThing,SomeOtherThing但这似乎并不优雅.
编辑:我想避免制作BaseThing通用.
Wil*_*ice 12
让我们先看看BaseThing你不想制作通用的课程:
public abstract class BaseThing {
public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer();
}
Run Code Online (Sandbox Code Playgroud)
这不是泛型类,但它包含泛型方法.通常,这样的泛型方法被设计为使得类型<T>由编译器基于该方法的一些参数来约束.例如:public <T> Class<T> classOf(T object).但在你的情况下,你的方法不需要参数.如果方法的实现从Collections实用程序类返回类似于此方法的"通用"泛型(我的术语),那么这也有点常见:public <T> List<T> emptyList().此方法不带参数,但类型<T>将从调用上下文推断出来; 它的工作原理只是因为实现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();
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,您希望abstract从基类重写该方法.只要返回类型在原始方法的上下文中仍然有效, Java中就允许覆盖返回类型.例如,您可以覆盖一个返回Number特定实现的方法,该实现始终Integer为该方法返回,因为它Integer 是一个 Number.
随着仿制药,然而,List<Integer> 是不是一个List<Number>.所以,当你的抽象方法定义为返回ThingDoer<T, String>(一些T extends BaseThing),你的重载返回ThingDoer<SomeThing, String>和ThingDoer<SomeOtherThing, String>一般不与一些不知名的兼容T,即使SomeThing和SomeOtherThing无论从扩展BaseThing.
调用者(来自抽象API)期望一些未知的,无法执行的T,无法通过任何具体实现来满足.实际上,您的具体重载不再是通用的(它们返回特定的,静态绑定的类型参数)并且与抽象类中的定义冲突.
编辑:定义抽象方法的"正确"方式(无警告)应该是这样的:
public abstract ThingDoer<? extends BaseThing, String> getThingDoer();
Run Code Online (Sandbox Code Playgroud)
这使得调用者明白它的ThingDoer第一个类型参数绑定到扩展的东西BaseThing(因此它可以像使用它一样使用它BaseThing),但调用者在抽象API访问时不会知道具体的实现.
编辑#2 - 我们在聊天中讨论的结果......
OP的原始示例用法是:
BaseThing thing = /* ... */;
thing.getThingDoer().do(thing);
Run Code Online (Sandbox Code Playgroud)
注意如何将相同的thing引用传递回从同一个东西的getThingDoer()方法返回的对象中的方法.返回的对象getThingDoer()需要紧密绑定到具体的实现类型thing(根据OP).对我来说,这闻起来像破碎的封装.
相反,我建议将逻辑操作公开为BaseThingAPI 的一部分,并将委托封装ThingDoer为内部实现细节.生成的API看起来像:
thing.doTheThing();
Run Code Online (Sandbox Code Playgroud)
并实施有点像:
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);
}
}
Run Code Online (Sandbox Code Playgroud)