切换instanceof?
我有一个使用instanceof
对象的switch case的问题:
例如:我的问题可以在Java中重现:
if(this instanceof A) doA(); else if(this instanceof B) doB(); else if(this instanceof C) doC():
如何使用switch...case
来实现呢?
这是子types多态性帮助的典型场景。 请执行下列操作
interface I { void do(); } class A implements I { void do() { doA() } ... } class B implements I { void do() { doB() } ... } class C implements I { void do() { doC() } ... }
然后你可以直接调用do()
。
如果您不能自由更改A
, B
和C
,则可以使用访问者模式来实现相同的function。
如果你绝对不能编码到一个接口,那么你可以使用枚举作为中介:
public A() { CLAZZ z = CLAZZ.valueOf(this.getClass().getSimpleName()); switch (z) { case A: doA(); break; case B: doB(); break; case C: doC(); break; } } enum CLAZZ { A,B,C; }
以防万一,如果有人会读它:
java中的最佳解决scheme是:
public enum Action { a{ void doAction(...){ // some code } }, b{ void doAction(...){ // some code } }, c{ void doAction(...){ // some code } }; abstract void doAction (...); }
这种模式的巨大好处是:
-
你只是这样做(根本没有开关):
void someFunction ( Action action ) { action.doAction(...); }
-
如果你添加新的行动称为“d”,你必须执行doAction(…)方法
注意:这个模式在Joshua的Bloch“Effective Java(2nd Edition)”
你不能。 switch
语句只能包含编译时常量和计算为整数(最多Java 6和Java 7中的string)的case
语句。
在函数式编程中你所要找的叫做“模式匹配”。
另请参阅避免Java中的instanceof
正如在顶级答案中所讨论的,传统的OOP方法是使用多态而不是开关。 这个技巧甚至有一个很好的文档重构模式: replace条件与多态性 。 每当我达到这个方法,我喜欢也实现一个空对象来提供默认行为。
从Java 8开始,我们可以使用lambdas和generics来给我们一些function程序员非常熟悉的东西:模式匹配。 这不是一个核心语言function,但是Java库提供了一个实现。 来自javadoc的示例:
Match.ofType(Number.class) .caze((Integer i) -> i) .caze((String s) -> new BigDecimal(s)) .orElse(() -> -1) .apply(1.0d); // result: -1
这不是Java世界中最自然的范例,所以谨慎使用它。 尽pipegenerics方法可以帮助您避免对匹配值进行types转换,但是我们却缺less一个标准的方法来分解匹配的对象,就像Scala的case类一样 。
不,没有办法做到这一点。 然而,你可能想要做的是把多态性作为处理这些问题的一种方法。
只需创build一个Map,其中的类是关键字,并且function(即lambda或类似)是值。
Map<Class,Runnable> doByClass = new HashMap<>(); doByClass.put(Foo.class, () -> doAClosure(this)); doByClass.put(Bar.class, this::doBMethod); doByClass.put(Baz.class, new MyCRunnable());
//当然,重构此只初始化一次
doByClass.get(getClass()).run();
如果您需要检查exception,而不是实现引发exception的FunctionalInterface,而不是使用Runnable。
我知道这是非常晚,但为未来的读者…
小心以上的方法仅基于A , B , C …类的名称 :
除非可以保证A , B , C …( Base的所有子类或实现者)是最终的,否则A , B , C …的子类将不会被处理。
尽pipeif,elseif,elseif ..方法对于大量的子类/实现者来说比较慢,但它更加准确。
使用像这样的switch语句不是面向对象的方式。 你应该使用多态的力量。 简单写一下
this.do()
以前build立了一个基类:
abstract class Base { abstract void do(); ... }
这是A
, B
和C
的基类:
class A extends Base { void do() { this.doA() } } class B extends Base { void do() { this.doB() } } class C extends Base { void do() { this.doC() } }
你不能只使用字节,short,char,int,String和枚举types(和原语的对象版本,它也取决于你的java版本,Strings可以在java 7中switch
)
这个怎么样 ?
switch (this.name) { case "A": doA(); break; case "B": doB(); break; case "C": doC(); break; default: console.log('Undefined instance'); }
如果你可以操作通用接口,你可以添加一个枚举,并让每个类都返回一个唯一的值。 您将不需要instanceof或访问者模式。
对我来说,逻辑需要写在switch语句中,而不是对象本身。 这是我的解决scheme:
ClassA, ClassB, and ClassC implement CommonClass
接口:
public interface CommonClass { MyEnum getEnumType(); }
枚举:
public enum MyEnum { ClassA(0), ClassB(1), ClassC(2); private int value; private MyEnum(final int value) { this.value = value; } public int getValue() { return value; }
IMPL:
... switch(obj.getEnumType()) { case MyEnum.ClassA: ClassA classA = (ClassA) obj; break; case MyEnum.ClassB: ClassB classB = (ClassB) obj; break; case MyEnum.ClassC: ClassC classC = (ClassC) obj; break; } ...
如果您使用的是java 7,则可以将枚举的string值和切换大小写块依然可用。
如果你需要通过“这个”对象的types“切换”,这个答案是最好的https://stackoverflow.com/a/5579385/2078368
但是,如果您需要将“开关”应用于任何其他variables。 我会build议另一个解决scheme。 定义如下界面:
public interface ClassTypeInterface { public String getType(); }
在想要“切换”的每个课程中实现此接口。 例:
public class A extends Something implements ClassTypeInterface { public final static String TYPE = "A"; @Override public String getType() { return TYPE; } }
之后,您可以按以下方式使用它:
switch (var.getType()) { case A.TYPE: { break; } case B.TYPE: { break; } ... }
你应该关心的唯一事情 – 保持types在实现ClassTypeInterface的所有类中是唯一的。 这不是一个大问题,因为在任何交集的情况下,您将收到“switch-case”语句的编译时错误。
有一种更简单的方法来模拟使用instanceof的开关结构,你可以通过在你的方法中创build一个代码块并用一个标签来命名它。 然后你使用结构来模拟case语句。 如果一个案例是真的,那么你使用中断LABEL_NAME摆脱你的临时切换结构。
DEFINE_TYPE: { if (a instanceof x){ //do something break DEFINE_TYPE; } if (a instanceof y){ //do something break DEFINE_TYPE; } if (a instanceof z){ // do something break DEFINE_TYPE; } }
我认为有理由使用switch语句。 如果你正在使用xText生成Code也许。 或另一种EMF生成的类。
instance.getClass().getName();
返回类实现名称的string。 即:org.eclipse.emf.ecore.util.EcoreUtil
instance.getClass().getSimpleName();
返回简单表示法,即:EcoreUtil
我个人喜欢下面的Java 1.8代码:
mySwitch("YY") .myCase("AA", (o) -> { System.out.println(o+"aa"); }) .myCase("BB", (o) -> { System.out.println(o+"bb"); }) .myCase("YY", (o) -> { System.out.println(o+"yy"); }) .myCase("ZZ", (o) -> { System.out.println(o+"zz"); });
会输出:
YYyy
示例代码使用string,但可以使用任何对象types,包括Class。 例如.myCase(this.getClass(), (0) -> ...
需要以下代码片段:
public Case mySwitch(Object reference) { return new Case(reference); } public class Case { private Object reference; public Case(Object reference) { this.reference = reference; } public Case myCase(Object b, OnMatchDo task) { if (reference.equals(b)) { task.task(reference); } return this; } } public interface OnMatchDo { public void task(Object o); }