从方法返回 Java 有界通用对象
Returning Java bounded generic objects from method
我知道 Oracle 教程和类似 How do I make the method return type generic? 的问题,但我仍然无法从 Java 方法 returning 通用对象。
简要示例:我有一个网络层次结构 Packet
和一个 Handler
层次结构,参数化为它们处理的 Packet
。最终我有一个 registry of Handler
s 其中包括一个方法 return 我是给定数据包的正确处理程序。
我想实现所有这些,理想情况下没有手动抑制的警告。
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);
}
}
希望这个例子相当清楚。
感谢您提供任何有用的建议,甚至是完整的重新设计策略。
在这里使用 Generic 没有意义:
- 首先,您在编码时永远不知道 return 对象的类型,这没有带来任何好处。
- 其次,使用泛型会造成混淆(对于案例
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 以支持不同类型的数据包。
有了这个,您可以在您的项目中添加更多的灵活性,因为您可以拥有任意数量的数据包类型和数据包处理程序,并且还可以隐藏真正的实现。还强制客户端仅使用您在界面中定义的 public API,从而限制您的风险。
您基本上有两个平行层次结构(Packet
和 PacketHandler
),其中一个层次结构中的每个级别都与另一个层次结构中的相同级别相关。下图显示了您的结构(跳过了 ThatHandler
)
Packet ------------> PacketHandler
^ ^
| |
ThisPacket --------> ThisPacketHandler
这些情况通常使用自引用类型参数来解决。另外,使用工厂方法而不是在注册表中创建对象。
这是你的 classes 结构的变化(另外,我猜你应该 Packet
class 摘要:
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);
现在,使用上述结构,您可以继续添加新的数据包,而无需修改现有的 classes。只需创建一个 Packet
和相应的 PacketHandler
。在新的Packetclass中重写getHandler()
方法,就可以使用了。现在还需要更改注册表 class。
我知道 Oracle 教程和类似 How do I make the method return type generic? 的问题,但我仍然无法从 Java 方法 returning 通用对象。
简要示例:我有一个网络层次结构 Packet
和一个 Handler
层次结构,参数化为它们处理的 Packet
。最终我有一个 registry of Handler
s 其中包括一个方法 return 我是给定数据包的正确处理程序。
我想实现所有这些,理想情况下没有手动抑制的警告。
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);
}
}
希望这个例子相当清楚。 感谢您提供任何有用的建议,甚至是完整的重新设计策略。
在这里使用 Generic 没有意义:
- 首先,您在编码时永远不知道 return 对象的类型,这没有带来任何好处。
- 其次,使用泛型会造成混淆(对于案例
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 以支持不同类型的数据包。
有了这个,您可以在您的项目中添加更多的灵活性,因为您可以拥有任意数量的数据包类型和数据包处理程序,并且还可以隐藏真正的实现。还强制客户端仅使用您在界面中定义的 public API,从而限制您的风险。
您基本上有两个平行层次结构(Packet
和 PacketHandler
),其中一个层次结构中的每个级别都与另一个层次结构中的相同级别相关。下图显示了您的结构(跳过了 ThatHandler
)
Packet ------------> PacketHandler
^ ^
| |
ThisPacket --------> ThisPacketHandler
这些情况通常使用自引用类型参数来解决。另外,使用工厂方法而不是在注册表中创建对象。
这是你的 classes 结构的变化(另外,我猜你应该 Packet
class 摘要:
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);
现在,使用上述结构,您可以继续添加新的数据包,而无需修改现有的 classes。只需创建一个 Packet
和相应的 PacketHandler
。在新的Packetclass中重写getHandler()
方法,就可以使用了。现在还需要更改注册表 class。