我知道Oracle教程和How do I make the method return type generic?之类的问题,但我仍然无法从Java方法返回通用对象。
简要示例:我有一个网络Packet
的层次结构和Handler
s的层次结构,参数化为它们处理的Packet
。最终我有Handler
的注册表,其中包含一个方法,可以返回给定数据包的正确处理程序。
我想实现所有这一切,理想情况下没有警告手动压制。
class Packet {}
class ThisPacket extends Packet {}
class ThatPacket extends Packet {}
interface PacketHandler<P extends Packet> {
boolean handle(P p);
}
class ThisPacketHandler extends PacketHandler<ThisPacket> {
boolean handle(ThisPacket p);
}
class ThatPacketHandler extends PacketHandler<ThatPacket> {
boolean handle(ThatPacket p);
}
这是非常规则的我相信,在我的实现中,我在中间有一些进一步的抽象类来塑造我的层次结构,但我认为现在可以忽略它。
关键部分是i)注册表:
class HandlersRegistry {
static <<RETURN TYPE>> getHandler(Packet p) {
if (p instanceof ThisPacket) return new ThisPacketHandler();
if (p instanceof ThatPacket) return new ThatPacketHandler();
return null;
}
}
<<RETURN TYPE>> OPTIONS (I tried):
// Raw-type Warning:
A. PacketHandler
// the registry user won't be able to use the handler:
B. PacketHandler<? extends Packet>
// Type mismatch: cannot convert from
C. PacketHandler<Packet>
..和ii)和注册用户:
class HandlerSwitchExample {
public static void main() {
// [...]
<<OBJECT TYPE>> handler = HandlersRegistry.getHandler(somePacket);
handler.handle(somePacket);
}
}
希望这个例子相当清楚。 感谢任何有用的建议,甚至完整的重新设计策略。
答案 0 :(得分:3)
您基本上有两个并行层次结构(Packet
和PacketHandler
),其中一个层次结构上的每个级别与其他层次结构中的相同级别相关。下图显示了您的结构(跳过ThatHandler
)
Packet ------------> PacketHandler
^ ^
| |
ThisPacket --------> ThisPacketHandler
这些案例通常使用自引用类型参数来解决。另外,使用工厂方法而不是在注册表中创建对象。
这里是你班级结构的变化(我想你应该让Packet
班级摘要:
abstract class Packet<T extends Packet<T>> {
/**
* Factory method.
* Override this method in sub-packets to return appropriate handlers
*/
abstract PacketHandler<T> getHandler();
}
然后你的子包就像:
class ThisPacket extends Packet<ThisPacket> {
@Override
PacketHandler<ThisPacket> getHandler() {
return new ThisPacketHandlerImpl();
}
}
class ThatPacket extends Packet<ThatPacket> {
@Override
PacketHandler<ThatPacket> getHandler() {
return new ThatPacketHandlerImpl();
}
}
现在,您的PacketHandlers
(您不需要单独的处理程序界面):
interface PacketHandler<P extends Packet<P>> {
boolean handle(P p);
}
class ThisPacketHandlerImpl implements PacketHandler<ThisPacket> {
@Override
public boolean handle(ThisPacket p) {
return false;
}
}
class ThatPacketHandlerImpl implements PacketHandler<ThatPacket> {
@Override
public boolean handle(ThatPacket p) {
return false;
}
}
最后是注册表:
class HandlersRegistry {
static <P extends Packet<P>> PacketHandler<P> getHandler(P p) {
return p.getHandler();
}
}
并像这样使用它:
ThisPacket somePacket = new ThisPacket();
PacketHandler<ThisPacket> handler = HandlersRegistry.getHandler(somePacket);
handler.handle(somePacket);
现在,通过上述结构,您可以继续添加新数据包,而无需修改现有类。只需创建Packet
和相应的PacketHandler
即可。覆盖新Packet类中的getHandler()
方法,您可以使用它。现在还需要更改注册表类。
答案 1 :(得分:1)
在这里使用 Generic 是没有意义的:
HandlerSwitchExample
,您不希望使用对象类型T
)。相反,只需使用继承
PacketHandler handler = HandlersRegistry.getHandler(somePacket);
和
static PacketHandler getHandler(Packet p) {
if (p instanceof ThisPacket) return new ThisPacketHandler();
if (p instanceof ThatPacket) return new ThatPacketHandler();
return null;
}
在HandlersRegistry
内,您可以实施strategy pattern以支持不同类型的数据包。
通过这种方式,您可以在项目中添加更多灵活性,因为您可以拥有所需数量的数据包和数据包处理程序,并隐藏真正的实现。还强制客户端仅使用您在界面中定义的公共API,从而限制您的风险。