Reputation: 1403
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
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
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
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