切换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/else
和instanceof
。
注意有几种类型具有相同的名称(内部类),因此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
,您的代码将继续工作 – 不需要您的努力。 如果使用类名,它将失败 – 无需编译器通知。
如果您反复切换同一个对象,您可能会发现在实现接口的包装类中将对象包装一次很有用。 此后,您可以简单地在界面上调用方法 – 没有if
, switch
或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
一个用例就是这样的,假设导入例如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 NullCase
其test
方法返回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 super " + ch + "> 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 super A> actionA) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } } public static void cswitch (Object o, Class classA, Consumer super A> actionA, Class classB, Consumer super B> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> actionC, Class classD, Consumer super D> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> actionC, Class classD, Consumer super D> actionD, Class classE, Consumer super E> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> actionC, Class classD, Consumer super D> actionD, Class classE, Consumer super E> actionE, Class classF, Consumer super F> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> actionC, Class classD, Consumer super D> actionD, Class classE, Consumer super E> actionE, Class classF, Consumer super F> actionF, Class classG, Consumer super G> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> actionC, Class classD, Consumer super D> actionD, Class classE, Consumer super E> actionE, Class classF, Consumer super F> actionF, Class classG, Consumer super G> actionG, Class classH, Consumer super H> 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 super A> actionA, Class classB, Consumer super B> actionB, Class classC, Consumer super C> 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 Consumer super A >
Consumer super A >
, Class< B >
与Consumer super B >
Consumer super B >
,依此类推。 使用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
注释与第一个示例相同。)