切换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

我想使用switch语句而不是if/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语句中调用ClassName.class.getCanonicalName() ,因为Java不允许这样做。 所以我的下一个解决方案是Enum。 这是我遇到问题的地方。

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

 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()已经完成。

这是一种根本不处理类名的方法,并且调度与switch语句一样快:创建一个哈希映射以将Class对象映射到特定于类的处理程序,并使用map而不是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 dispatch = new HashMap(); // 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 } 

使用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  void switchType(Object o, Consumer... a) { for (Consumer consumer : a) consumer.accept(o); } public static  Consumer caze(Class cls, Consumer c) { return obj -> Optional.of(obj).filter(cls::isInstance).map(cls::cast).ifPresent(c); } 

当您不拥有类时, instanceof运算符是一种简单的方法。 当对象是给定类或子类时, instanceof表达式为true。

你提到你没有这些课程。 所有者可以在后续版本中引入子类。 假设所有者将APlus作为A的子类引入.APlus的实例是A.在A上工作的代码也应该在APlus上工作。 如果您使用instanceof ,您的代码将继续工作 – 不需要您的努力。 如果使用类名,它将失败 – 无需编译器通知。

如果您反复切换同一个对象,您可能会发现在实现接口的包装类中将对象包装一次很有用。 此后,您可以简单地在界面上调用方法 – 没有ifswitch或map。

 public interface IWrapper { public void handle(); public String describe(); } public AWrapper implements IWrapper { ... } public BWrapper implements IWrapper { ... } public CWrapper implements IWrapper { ... } public UnknownWrapper implements IWrapper { ... } IWrapper wrap( Object o ) { if ( o instanceof A ) return new AWrapper((A) o); else if ( o instanceof B ) return new BWrapper((B) o); else if ( o instanceof C ) return new CWrapper((C) o); else return new UnknownWrapper(o); } 

即使在保证缺少子类的情况下,也避免在switch情况下将类名指定为文字字符串。 这允许编译器找不到的错误,这可能会花费您调试时间。

你非常接近使用枚举的解决方案。 它没有编译,因为你的枚举错过了从String映射枚举的构造函数和coversion方法。 实际上即使没有String也可以解决它,即根本不调用getCanonicalName:

 public enum Classes { // changed enum constants a bit to avoid confusing with target class names ClsA (A.class), ClsB (B.class), ClsC (C.class), UNKNOWN(null); private final Class targetClass; Classes(Class targetClass) { this.targetClass = targetClass; } public static Classes fromClass(Class cls) { for(Classes c : values()) { if(c.targetClass == cls) return c; } return UNKNOWN; } } switch (Classes.fromClass(o.getClass())) { case ClsA: handleA((A)o); break; case ClsB: handleB((B)o); break; case ClsC: handleC((C)o); break; default: handleUnknown(o); break; } 

如果您获得了已知类的重要数量,请考虑使用map而不是在Classes.fromClass中迭代,例如:

 public enum Classes { ClsA(A.class), ClsB(B.class), // etc... UNKNWON(null); // need a wrapper class to avoid compilation problem // with referring static enum field within an initializer private static class Holder { public static final IdentityHashMap, Classes> map = new IdentityHashMap<>(); } Classes(Class targetClass) { Holder.map.put(targetClass, this); } public static Classes fromClass(Class cls) { Classes c = Holder.map.get(cls); return c != null ? c : UNKNOWN; } } 

我能够使用java.lang.reflect

 import java.lang.reflect.Method; public class MyClass { public void validate(Object o) { String className = o.getClass().getSimpleName(); try { //this line searches a method named as className Method m = this.getClass().getDeclaredMethod(className); //this line execute the method m.invoke(this); } catch (Exception e) { e.printStackTrace(); handleUnknown(); } } //this methot will execute if the object o is instance of A public void A() { } //this methot will execute if the object o is instance of B public void B() { } //this methot will execute if the object o is instance of C public void C() { } //this methot will execute if the method is unknown public void handleUnknown(){ } } 

要切换已知的类类型,您可以使用以下方法

使用类名创建枚举

 public enum ClassNameEnum { ClassA, ClassB, ClassC } 

找到对象的类名称 。 在枚举上写一个开关盒。

 private void switchByClassType(Object obj) { ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName()); switch (className) { case ClassA: doA(); break; case ClassB: doB(); break; case ClassC: doC(); break; } } } 

这是一个例子,它为每种情况使用一个简单的对象。

 package mcve.util; import java.util.*; import java.util.function.*; /** * Allows switch-like statements with classes and consumers. */ public final class ClassSwitch implements Consumer { /** * For each of the specified cases, in order of their * appearance in the array, if cases[i].test(obj) returns * true, then invoke cases[i].accept(obj) and return. * * @param obj the object to switch upon * @param cases the cases for the switch * @throws NullPointerException * if any of the cases are null */ public static void cswitch(Object obj, Case... cases) { if (cases != null) { for (Case c : cases) { if (c.test(obj)) { c.accept(obj); break; } } } } /** * @param type the type of the case * @param action the action to perform * @param  the type of the case * @throws NullPointerException * if the type or action is null * @return a new Case */ public static  Case ccase(Class type, Consumer action) { return new Case<>(type, action); } /** * @param  the type of the case */ public static final class Case implements Predicate, Consumer { private final Class type; private final Consumer action; /** * @param type the type of the case * @param action the action to perform * @throws NullPointerException * if the type or action is null */ public Case(Class type, Consumer action) { this.type = Objects.requireNonNull(type, "type"); this.action = Objects.requireNonNull(action, "action"); } /** * @param obj the object to test * @return true if the object is an instance of T, else false */ @Override public boolean test(Object obj) { return type.isInstance(obj); } /** * @param obj the object to perform the action on * @throws ClassCastException * if the object is not an instance of T */ @Override public void accept(Object obj) { action.accept(type.cast(obj)); } } /** * An unmodifiable list of the cases in this switch. */ private final List> cases; /** * @param cases the cases for this switch * @throws NullPointerException * if any of the cases are null */ public ClassSwitch(Case... cases) { if (cases == null) { this.cases = Collections.emptyList(); } else { List> list = new ArrayList<>(cases.length); for (Case c : cases) { list.add(Objects.requireNonNull(c, "case")); } this.cases = Collections.unmodifiableList(list); } } /** * @return an unmodifiable view of the cases in this switch */ public List> getCases() { return cases; } /** * For each of the cases in this switch, in order of their * appearance in the list, if cases.get(i).test(obj) returns * true, then invoke cases.get(i).accept(obj) and return. * * @param obj the object to switch upon */ @Override public void accept(Object obj) { for (Case c : cases) { if (c.test(obj)) { c.accept(obj); break; } } } } 

一个用例就是这样的,假设导入例如import static mcve.util.ClassSwitch.*;

 cswitch(anObject, ccase(Byte.class, b -> System.out.println("Byte")), ccase(Short.class, s -> System.out.println("Short")), ccase(Integer.class, i -> System.out.println("Integer")), ccase(Long.class, l -> System.out.println("Long")), ccase(Float.class, f -> System.out.println("Float")), ccase(Double.class, d -> System.out.println("Double")) ); 

您还可以创建可重用的对象:

 ClassSwitch ts = new ClassSwitch(ccase(String.class, System.out::println), ccase(Double.class, System.out::println)); ts.accept(anObject); 

笔记:

  • 如果需要default大小写,可以使用Object.class作为最后一种情况。

  • 没有办法处理null的情况,但可以稍微修改一下。 你可以创建一个class NullCasetest方法返回obj == null


您还可以做的是实际生成重载而不是使用varargs。 这使您可以使用通用方法声明将类与使用者关联。 以下是一个相当简单的例子:

 package mcve.util; import java.util.*; import java.util.function.*; /** * Allows switch-like statements with classes and consumers. */ public final class GeneratedClassSwitch { private GeneratedClassSwitch() {} /** * Generates overloads for a class switch to System.out. * * For example, if max=4, then 5 methods are generated: * with 0, 1, 2, 3, and 4 cases. * * @param max * the number of cases in the largest overload generated * @param indents * the number of indents to indent each generated method * @throws IllegalArgumentException * if max is negative or greater than 26, or if indents * is negative */ public static void generateFixedOverloads(int max, int indents) { if (max < 0 || max > 26) { throw new IllegalArgumentException("max=" + max); } String indent = String.join("", Collections.nCopies(indents, " ")); for (int i = 0; i <= max; ++i) { System.out.print(indent); System.out.print("public static "); if (i > 0) { System.out.print("<"); for (char ch = 'A'; ch < 'A' + i; ++ch) { if (ch != 'A') { System.out.print(", "); } System.out.print(ch); } System.out.print("> "); } System.out.print("void cswitch"); if (i > 0) { System.out.println(); System.out.print(indent + " (Object o, "); for (char ch = 'A'; ch < 'A' + i; ++ch) { if (ch != 'A') { System.out.println(","); System.out.print(indent + " "); } System.out.print("Class<" + ch + "> class" + ch); System.out.print(", Consumer action" + ch); } } else { System.out.print("(Object o"); } System.out.println(") {"); for (char ch = 'A'; ch < 'A' + i; ++ch) { if (ch == 'A') { System.out.print(indent + " "); } else { System.out.print(" else "); } System.out.println("if (class" + ch + ".isInstance(o)) {"); System.out.print(indent + " "); System.out.println("action" + ch + ".accept(class" + ch + ".cast(o));"); System.out.print(indent + " "); System.out.print("}"); if (ch == ('A' + i - 1)) { System.out.println(); } } System.out.print(indent); System.out.println("}"); } } // Generated code pasted below. public static void cswitch(Object o) { } public static  void cswitch (Object o, Class classA, Consumer actionA) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC, Class classD, Consumer actionD) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC, Class classD, Consumer actionD, Class classE, Consumer actionE) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC, Class classD, Consumer actionD, Class classE, Consumer actionE, Class classF, Consumer actionF) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC, Class classD, Consumer actionD, Class classE, Consumer actionE, Class classF, Consumer actionF, Class classG, Consumer actionG) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } else if (classG.isInstance(o)) { actionG.accept(classG.cast(o)); } } public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC, Class classD, Consumer actionD, Class classE, Consumer actionE, Class classF, Consumer actionF, Class classG, Consumer actionG, Class classH, Consumer actionH) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } else if (classG.isInstance(o)) { actionG.accept(classG.cast(o)); } else if (classH.isInstance(o)) { actionH.accept(classH.cast(o)); } } } 

如果要生成重载,例如超过8个案例,可以说如下所示:

 GeneratedClassSwitch.generateFixedOverloads(16, 1); 

这将生成System.out方法,遵循以下的一般forms:

 public static  void cswitch (Object o, Class classA, Consumer actionA, Class classB, Consumer actionB, Class classC, Consumer actionC) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } } 

请注意,我们能够将每个类类型映射到其关联的使用者类型,即Class< A > with ConsumerA > ConsumerA >Class< B >ConsumerB > ConsumerB > ,依此类推。 使用varargs实际上是不可能的(截至当前版本的Java,无论如何,这是10)。

我们的使用示例现在是这样的,再次假设导入例如import static mcve.util.GeneratedClassSwitch.*;

 cswitch(anObject, Byte.class, b -> System.out.println("Byte"), Short.class, s -> System.out.println("Short"), Integer.class, i -> System.out.println("Integer"), Long.class, l -> System.out.println("Long"), Float.class, f -> System.out.println("Float"), Double.class, d -> System.out.println("Double") ); 

(关于default情况和null注释与第一个示例相同。)