检查班级类型

时间:2014-06-05 16:26:38

标签: java

我如何检查某个类是否属于确定类型,例如:

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

3 个答案:

答案 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