Emax
Emax

Reputation: 1403

Check class type

How i can check if a class is of a determinated type for example:

// PacketHandler.java

public interface PacketHandler<T> {
    public void handlePacket(T packet);
}


// PacketReader.java

public void read() {
    Packet packet = // Read some input
    for(PacketHandler packetHandler : packetHandlers) {
        if  (packetHandler is the type of packet) { // here is the problem
            packetHandler.handlePacket(packet);
        }
    }
}

public void registerHandler(PacketHandler<?> packetHandler) {
    packetHandlers.add(packetHandler);
}

// Main

packetReader.registerHandler(new PacketHandler<RandomPacket>() {
    public void handlePacket(RandomPacket packet) {
        // I handle the packet
    }
});

I know that this question maybe seems stupid; but how to solve this problem?

**Edit*****

Jon Skeet, so the class should be:

public class RandomClass implements PacketHandler {
    public boolean handlePacket(Packet packet) {
       if (packet instanceof PacketThatThisClassHandle) {
          //handle with casting
          return true;
       } else {
          return false;
       }
    }
}

Upvotes: 3

Views: 310

Answers (3)

Jon Skeet
Jon Skeet

Reputation: 1500155

Unfortunately Java generics use type erasure, meaning that at execution time, any particular PacketHandler<T> is just PacketHandler as far as the VM is concerned.

You may want to change your code to:

public interface PacketHandler {
    // The parameter type can  be Object if you really want
    boolean tryHandlePacket(Packet packet);
}

... and make a PacketHandler just return false if it doesn't know how to handle a particular packet type.

Then you can just use:

for (PacketHandler handler : handlers) {
    if (handler.tryHandlePacket(packet)) {
        break;
    }
}

(That's assuming you only want a single handler to actually handle any packet type.)

If you still want a generic interface, you'd either need a boolean handlesPacket(Packet) method, or possibly a Class<T> getPacketType() method. Either way it's still going to be a pain in terms of casting the packet to the right type...

If you have lots of packet handlers, you could potentially create an abstract base class:

public abstract class AbstractPacketHandler<T extends Packet>
    implements PacketHandler {

    private final Class<T> packetType;

    protected AbstractPacketHandler(Class<T> packetType) {
        this.packetType = packetType;
    }

    protected abstract void handlePacket(T packet);

    public boolean tryHandlePacket(Packet packet) {
        if (!packetType.isInstance(packet)) {
            return false;
        }
        handlePacket(packetType.cast(packet));
        return true;
    }
}

Then you can write:

public class FooPacketHandler extends PacketHandler<Foo> {
    public FooPacketHandler() {
        super(Foo.class);
    }

    protected void handlePacket(Foo packet) {
        ...
    }
}

Upvotes: 4

Jack
Jack

Reputation: 133567

Type erasure won't make this attempt easy. The mapping part it's quite easy, you can use a HashMap. But the problem is that the handlePacket method accepts a parameter of type T, which forces you to cast the object to that type before passing it to the handler.

To avoid relaxing the constraint you could use a two level invokation, something like:

  interface Packet { }
  class ConcretePacket implements Packet { }

  HashMap<Class<?>, PacketHandler<?>> mapping = 
                new HashMap<Class<?>, PacketHandler<?>>();

  public abstract class PacketHandler<T extends Packet> {        
    PacketHandler(Class<T> clazz) {
      mapping.put(clazz, this);
    }

    public final void handlePacket(Packet packet) {
      doHandlePacket((T)packet);
    }

    public abstract void doHandlePacket(T packet);
  }

  public class ConcretePacketHandler extends PacketHandler<ConcretePacket>
  {
    ConcretePacketHandler()
    {
      super(ConcretePacket.class);
    }

    public void doHandlePacket(ConcretePacket s) {
      // whatever
    }
  }

  public void receivedPacket(Packet packet) {
    PacketHandler<?> handler = mapping.get(packet.getClass());
    if (handler != null)
      handler.handlePacket(packet);
  }

Mind that this could not work in certain situations (maybe with different classloaders involved) and that, to manage subclasses of PacketManager, you will need to find a better way to retrieve the correct handler, eg by using a List<Pair<Class<?>,PacketHandler<?>>> so that you can check

if (listElement.clazz.isAssignableFrom(packet.getClass()))
  handler = listElement.handler;

maybe even using priorities so that the exact class is found before a possible superclass.

Upvotes: 3

Mitch Connor
Mitch Connor

Reputation: 776

I don't exactly understand what you are trying to get but you could possibly use:

if ( type.isInstance(obj) ) {
    //do something
}

http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29

Upvotes: 1

Related Questions