我如何检查某个类是否属于确定类型,例如:
// 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
}
});
我知道这个问题可能看起来很愚蠢; 但是如何解决这个问题呢?
** *****编辑
Jon Skeet,所以课程应该是:
public class RandomClass implements PacketHandler {
public boolean handlePacket(Packet packet) {
if (packet instanceof PacketThatThisClassHandle) {
//handle with casting
return true;
} else {
return false;
}
}
}
答案 0 :(得分:4)
不幸的是,Java泛型使用type erasure,这意味着在执行时,就VM而言,任何特定的PacketHandler<T>
只是PacketHandler
。
您可能希望将代码更改为:
public interface PacketHandler {
// The parameter type can be Object if you really want
boolean tryHandlePacket(Packet packet);
}
...如果PacketHandler
不知道如何处理特定的数据包类型,请使false
返回for (PacketHandler handler : handlers) {
if (handler.tryHandlePacket(packet)) {
break;
}
}
。
然后你可以使用:
boolean handlesPacket(Packet)
(假设您只需要一个处理程序来实际处理任何数据包类型。)
如果您仍然需要通用接口,则需要Class<T> getPacketType()
方法或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;
}
}
方法。无论哪种方式,将数据包转换为正确的类型仍然会很痛苦......
如果你有很多数据包处理程序,你可能会创建一个抽象基类:
public class FooPacketHandler extends PacketHandler<Foo> {
public FooPacketHandler() {
super(Foo.class);
}
protected void handlePacket(Foo packet) {
...
}
}
然后你可以写:
{{1}}
答案 1 :(得分:3)
类型擦除不会使这种尝试变得容易。映射部分非常简单,您可以使用HashMap
。但问题是handlePacket
方法接受类型为T
的参数,这会强制您在将对象传递给处理程序之前将其强制转换为该类型。
为避免放松约束,您可以使用两级调用,例如:
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);
}
请注意,这在某些情况下可能不起作用(可能涉及不同的类加载器),并且为了管理PacketManager
的子类,您需要找到一种更好的方法来检索正确的处理程序,例如使用List<Pair<Class<?>,PacketHandler<?>>>
以便您可以查看
if (listElement.clazz.isAssignableFrom(packet.getClass()))
handler = listElement.handler;
甚至可能使用优先级,以便在可能的超类之前找到确切的类。
答案 2 :(得分:1)
我并不完全明白你想要得到什么,但你可以使用:
if ( type.isInstance(obj) ) {
//do something
}
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29