4

I am aware of Oracle tutorials and questions like How do I make the method return type generic? but still I am having trouble returning generic objects from a Java method.

Brief example: I have a hierarchy of network Packets and a hierarchy of Handlers, parametrized to the Packet they handle. Eventually I have a registry of Handlers which includes a method to that would return me the proper handler of a given packet.

I would like to implement all of this with ideally no warning to manually suppress.

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);
}

This is quite regular I believe, in my implementation I have some further abstract classes in the middle to shape my hierarchy, but I think this can be ignored by now.

The critical part is i) the registry:

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> 

..and ii) and the registry user:

class HandlerSwitchExample {
    public static void main() {
        // [...]
        <<OBJECT TYPE>> handler = HandlersRegistry.getHandler(somePacket);
        handler.handle(somePacket);
    }
}

Hope the example is fairly clear. Thanks for any helpful suggestion, even complete redesign strategies.

Community
  • 1
  • 1
Campa
  • 4,267
  • 3
  • 37
  • 42
  • Wouldn't something like `static

    PacketHandler

    getHandler( P p)` work?

    – biziclop Mar 18 '15 at 10:43
  • I'm surely misunderstanding your problem, but <> = Packet and <> = Handler – m0skit0 Mar 18 '15 at 10:44
  • @biziclop: `Type mismatch: cannot convert from ThisPacketHandler to PacketHandler

    `.

    – Campa Mar 18 '15 at 10:57
  • @m0skit0: if you mean `<>=>>OBJECT TYPE>>=PacketHandler`), yes, but I get raw-type warnings ! – Campa Mar 18 '15 at 11:01
  • 1
    One crucial question here is whether the "user" (who passes the packet to the registry) knows the packet by its *contrete* type, or only by its *base* type. If he knows it by its *real* type (that is, if he has a `ThisPackage p` and not only a `Package p`), then this would probably be rather simple. But I assume that this is not the case (guessing from the variable name "**some** Package") – Marco13 Mar 18 '15 at 11:44

2 Answers2

3

You basically have two parallel hierarchies (Packet and PacketHandler), where each level on one hierarchy is related to the same level in other hierarchy. Below diagrams shows your structure (Skipped the ThatHandler)

  Packet ------------> PacketHandler
    ^                        ^
    |                        |
ThisPacket --------> ThisPacketHandler

These cases are often solved using self-referential type parameters. Also, use factory method instead of creating objects in registry.

Here's the change in structure of your classes (also, I guess you should make Packet class abstract:

abstract class Packet<T extends Packet<T>> {
  /**
   *  Factory method.
   *  Override this method in sub-packets to return appropriate handlers
   */
  abstract PacketHandler<T> getHandler();
}

Then your sub-packets would be like:

class ThisPacket extends Packet<ThisPacket> {
  @Override
  PacketHandler<ThisPacket> getHandler() {
    return new ThisPacketHandlerImpl();
  }
}

class ThatPacket extends Packet<ThatPacket> {
  @Override
  PacketHandler<ThatPacket> getHandler() {
    return new ThatPacketHandlerImpl();
  }
}

Now, your PacketHandlers (You don't need separate handler interfaces here):

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;
  }
}

Finally the registry:

class HandlersRegistry {
  static <P extends Packet<P>> PacketHandler<P> getHandler(P p) {
    return p.getHandler();
  }
}

And use it like this:

ThisPacket somePacket = new ThisPacket();
PacketHandler<ThisPacket> handler = HandlersRegistry.getHandler(somePacket);
handler.handle(somePacket);

Now, with the above structure, you can keep on adding new Packets without having the need to modify existing classes. Just create a Packet and corresponding PacketHandler. Override the getHandler() method in the new Packet class, and you're good to use it. Now need to change the registry class also.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Gosh.. thanks. I think I'll try out this cute nasty pattern, but just one question: if the registry user gets the packet from an other method `Packet getPacket()`, all this mess (including mine and Pham's) will not work out since the received packet, whatever the subtype, gets erased to its upper bound. ? – Campa Mar 18 '15 at 11:16
  • 1
    @Campa The issue with generics is, to make it work with other things, you've to make the other things also generic. So, your `getPacket()` method should also be generic. How you're going to implement that depends upon where would you have that method. But ideally your registry should only create one object, either the handler or the packet. – Rohit Jain Mar 18 '15 at 11:18
  • It's hard to judge from the short, high-level description, but **if** the structure is intended to be as suggested by the class names, then letting the `Package` create its own `Handler` looks like a **horrible** idea to me from an OOD point of view (and, by the way, makes the registry somewhat superflous...) – Marco13 Mar 18 '15 at 11:43
  • @Marco13 Sure the registry would be superfluous in this case, and I could happily drop it. Not sure why this solution is horrible: any alternative idea..? – Campa Mar 18 '15 at 11:58
  • @Campa Maybe "horrible" was too strong - it ~"just seems not *right*", in the sense of http://en.wikipedia.org/wiki/Separation_of_concerns . Referring to the design, you should see the drawbacks: You can not have different types of `PackageHandler` for the *same* type of `Package`. Each implementor of `Package` must provide its own `Handler`. I thought about cases like a `StringPackage`, and one client might need a `PrintToConsoleHandler implements Handler`, and another might need a `PrintToFileHandler implements Handler`, but maybe thats no use case for you. – Marco13 Mar 18 '15 at 12:08
  • @Campa ... but, regarding that question: I don't know an alternative solution that avoids unchecked casts (although I think that it should be possible to make it type-safe nevertheless - even if the compiler does not *see* that it is type-safe, and his warnings have to be suppressed). – Marco13 Mar 18 '15 at 12:10
  • @RohitJain: may you also justify the use of factories here? I know it is somehow a general good practice, but I tend to prefer strong reasons before using factories, not to obfuscate the use of a class. Thanks, and thanks for your time. – Campa Mar 18 '15 at 15:39
  • @Campa The `getHandler()` of registry is doing nothing but returning different instances of handlers based on instance of `Packet`. Ideal way of doing this is to have a `Map, Packet>` in the registry, and register the instances of `Packet` there. But that would be major change. However, this is the perfect case of factory pattern, else you would have to have `if-else` in the `getHandler()` method, to return actual handler instance based on type of `Packet`. That would be more cumbersome IMO. – Rohit Jain Mar 18 '15 at 15:48
1

Using Generic here is not making sense:

  • First, you never know the type of return object in coding time, which didn't bring any benefit.
  • Second, using generic will cost confusion (for case HandlerSwitchExample, you dont want to work with an object type T).

So instead, just use inheritance

PacketHandler handler = HandlersRegistry.getHandler(somePacket);

And

static PacketHandler getHandler(Packet p) {
    if (p instanceof ThisPacket) return new ThisPacketHandler();
    if (p instanceof ThatPacket) return new ThatPacketHandler();
    return null;
}

Inside the HandlersRegistry, you can implement strategy pattern to support different type of packet.

With this, you can add more flexibilities in your project, as you can have as many as you want number of type of packet and packet handler, and also hide the real implementation. Also force client to work with only the public API that you defined in the interface, thus, limiting your risk.

Pham Trung
  • 11,204
  • 2
  • 24
  • 43
  • Not sure who downvoted your answer (not me), hope he/she will come up with an explanation. It seems to me your solution makes sense but as long as _this_ and _that_ packet share a common ground which is enough for _this_ and _that_ handler, otherwise an explicit cast would be needed. ? – Campa Mar 18 '15 at 11:05
  • @m0skit0 no problem, you can undo now :) ( just edited my answer) – Pham Trung Mar 18 '15 at 11:06