在 java 中切换类型

在我开始之前,我知道这个问题有很多答案,可以提出替代方法。我正在寻求这种特定方法的帮助,以确定它是否可能,如果没有,类似的方法可能有效。

我有一个方法,它采用一个超类,并根据传递对象的类型调用一个方法。例如:

public void handle(Object o){
  if (o instanceof A)
    handleA((A)o);
  else if (o instanceof B)
    handleB((B)o);
  else if (o instanceof C)
    handleC((C)o);
  else 
    handleUnknown(o);

我无法修改子类型以覆盖方法,正如这个答案所暗示的那样,因为我不拥有这些类。所以方法就是我所拥有的一切。handle()instanceof

我想使用语句而不是 ,仅仅是因为它更整洁。我知道你只能打开基元和字符串,所以我正在切换类名:switchif/else

switch(o.getClass().getCanonicalName()){
case "my.package.A":
  handleA((A)o);
  break;
case "my.package.B":
  handleB((B)o);
  break;
case "my.package.C":
  handleC((C)o);
  break;
default:
  handleUnknown(o);
  break;
}

这里的问题是规范名称非常长(如12个子包),我不能在case语句中调用,因为Java不允许这样做。所以我的下一个解决方案是枚举。这就是我遇到问题的地方。ClassName.class.getCanonicalName()

我希望我的代码看起来像这样:

public enum Classes {
  A (A.getCanonicalName()),
  B (B.getCanonicalName()),
  C (C.getCanonicalName());
}

switch (o.getClass().getCanonicalName()){
case Classes.A:
  handleA((A)o);
  break;
case Classes.B:
  handleB((B)o);
  break;
case Classes.C:
  handleC((C)o);
  break;
default:
  handleUnknown(o);
  break;
}

但这不会编译。我不知道为什么。我想要一些方法,允许我切换类型,而不必键入整个规范名称。如果我这样做,我不妨只使用 和 。if/elseinstanceof

注意有几个类型具有相同的名称(内部类),因此已出局。getSimpleName()


答案 1

这是一种根本不处理类名的方法,并且调度速度与语句一样快:制作一个哈希映射以将对象映射到特定于类的处理程序,并使用映射而不是:switchClass<T>switch

// Declare an interface for your polymorphic handlers to implement.
// There will be only anonymous implementations of this interface.
private interface Handler {
    void handle(Object o);
}
// Make a map that translates a Class object to a Handler
private static final Map<Class,Handler> dispatch = new HashMap<Class,Handler>();
// Populate the map in a static initializer
static {
    dispatch.put(A.class, new Handler() {
        public void handle(Object o) {
            handleA((A)o);
        }
    });
    dispatch.put(B.class, new Handler() {
        public void handle(Object o) {
            handleB((B)o);
        }
    });
    dispatch.put(C.class, new Handler() {
        public void handle(Object o) {
            handleC((C)o);
        }
    });
}
// This object performs the dispatch by looking up a handler,
// and calling it if it's available
private static void handle(Object o) {
    Handler h = dispatch.get(o.getClass());
    if (h == null) {
        // Throw an exception: unknown type
    }
    h.handle(o); // <<== Here is the magic
}

答案 2

使用java 8 lambdas,你可以得到这样的东西:

Collection col = Arrays.asList(1,2,3);
switchType(col, 
       caze(Collection.class, c->System.out.println(c.size())),
       caze(ArrayBlockingQueue.class, bq->System.out.println(bq.remainingCapacity())),
       caze(Queue.class, q->System.out.println(q.poll())),
       caze(String.class, s->System.out.println(s.substring(0))),
       caze(ArrayList.class, al->System.out.println(al.get(0)))
);

为此,您应该定义以下静态方法:

public static <T> void switchType(Object o, Consumer... a) {
    for (Consumer consumer : a)
        consumer.accept(o);
}

public static <T> Consumer caze(Class<T> cls, Consumer<T> c) {
    return obj -> Optional.of(obj).filter(cls::isInstance).map(cls::cast).ifPresent(c);
}