从方法返回Java有界通用对象

时间:2015-03-18 10:36:17

标签: java generics inheritance

我知道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);
    }
}

希望这个例子相当清楚。 感谢任何有用的建议,甚至完整的重新设计策略。

2 个答案:

答案 0 :(得分:3)

您基本上有两个并行层次结构(PacketPacketHandler),其中一个层次结构上的每个级别与其他层次结构中的相同级别相关。下图显示了您的结构(跳过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,从而限制您的风险。