是否可以在switch语句中使用instanceof运算符?

时间:2011-04-07 10:03:29

标签: java switch-statement instanceof

我有一个使用instanceof对象的开关案例的问题:

例如:我的问题可以用Java重现:

if(this instanceof A)
    doA();
else if(this instanceof B)
    doB();
else if(this instanceof C)
    doC():

如何使用switch...case实现?

24 个答案:

答案 0 :(得分:201)

这是子类型多态性有帮助的典型场景。请执行以下操作

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()上致电this即可。

如果您无法更改ABC,则可以应用访问者模式来实现相同目标。

答案 1 :(得分:81)

如果您绝对无法对接口进行编码,那么您可以使用枚举作为中介:

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;

}

答案 2 :(得分:34)

以防万一有人会阅读它:

java中的最佳解决方案是:

public enum Action { 
    a{
        void doAction(...){
            // some code
        }

    }, 
    b{
        void doAction(...){
            // some code
        }

    }, 
    c{
        void doAction(...){
            // some code
        }

    };

    abstract void doAction (...);
}

这种模式的好处是:

  1. 你就是这样(根本没有开关):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
    
  2. 如果你添加名为“d”的新动作,你必须使用非法的行动(...)方法

  3. 注意:这种模式在Joshua的Bloch“Effective Java(第2版)”

    中有描述

答案 3 :(得分:24)

只需创建一个Map,其中类是键,而函数,即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();

如果您需要检查异常,而不是实现抛出异常的FunctionalInterface并使用它而不是Runnable。

答案 4 :(得分:16)

你做不到。 switch语句只能包含case语句,这些语句是编译时常量并且计算为整数(最多Java 6和Java 7中的字符串)。

您正在寻找的是在函数式编程中称为“模式匹配”。

另见Avoiding instanceof in Java

答案 5 :(得分:16)

正如前面的答案中所讨论的,传统的OOP方法是使用多态而不是切换。对于这个技巧,甚至有一个记录良好的重构模式:Replace Conditional with Polymorphism。每当我实现这种方法时,我都希望实现Null object来提供默认行为。

从Java 8开始,我们可以使用lambdas和泛型来为我们提供一些函数式程序员非常熟悉的东西:模式匹配。它不是核心语言功能,但Javaslang library提供了一种实现。 javadoc的示例:

Match.ofType(Number.class)
    .caze((Integer i) -> i)
    .caze((String s) -> new BigDecimal(s))
    .orElse(() -> -1)
    .apply(1.0d); // result: -1

这不是Java世界中最自然的范例,因此谨慎使用它。虽然泛型方法可以避免必须对匹配的值进行类型转换,但我们缺少一种标准方法来分解匹配的对象,例如Scala's case classes

答案 6 :(得分:7)

我知道这已经很晚了,但对于未来的读者来说......

请注意上述仅基于 A B C 名称的方法 ...:

除非您能保证 A B C ...( Base )最终然后 A B C ...的子类将不会被处理。< / p>

即使 if,elseif,elseif .. 方法对于大量子类/实现者来说速度较慢,但​​它更准确。

答案 7 :(得分:5)

使用像这样的switch语句不是面向对象的方式。您应该使用多态的强大功能。只需写下

this.do()

之前已设置基类:

abstract class Base {
   abstract void do();
   ...
}

这是ABC的基类:

class A extends Base {
    void do() { this.doA() }
}

class B extends Base {
    void do() { this.doB() }
}

class C extends Base {
    void do() { this.doC() }
}

答案 8 :(得分:5)

不,没有办法做到这一点。但是,您可能希望将 Polymorphism 视为解决此类问题的方法。

答案 9 :(得分:3)

我个人喜欢以下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

示例代码使用Strings,但您可以使用任何对象类型,包括Class。例如.myCase(this.getClass(), (o) -> ...

需要以下代码段:

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

答案 10 :(得分:3)

如果您可以操作公共接口,则可以添加枚举并让每个类返回唯一值。您不需要instanceof或访客模式。

对我来说,逻辑需要在switch语句中写入,而不是对象本身。这是我的解决方案:

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,则可以为枚举设置字符串值,并且切换案例块仍然有效。

答案 11 :(得分:2)

这个怎么样?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}

答案 12 :(得分:2)

你不能只使用byte,short,char,int,String和枚举类型(以及基元的对象版本,它也取决于你的java版本,字符串可以是switch在java 7中编辑

答案 13 :(得分:1)

If you need to "switch" thru the class type of "this" object, this answer is the best https://stackoverflow.com/a/5579385/2078368

但如果你需要申请&#34;切换&#34;到任何其他变量。我建议另一个解决方案。定义以下界面:

public interface ClassTypeInterface {
    public String getType();
}

在您想要的每个班级中实施此界面&#34;切换&#34;。例如:

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

你唯一应该关心的事情 - 保持&#34;类型&#34;在实现ClassTypeInterface的所有类中都是唯一的。这不是一个大问题,因为在任何交叉点的情况下,你会收到&#34; switch-case&#34;的编译时错误。言。

答案 14 :(得分:1)

有一种更简单的方法来模拟使用instanceof的开关结构,您可以通过在方法中创建代码块并使用标签命名它来实现此目的。然后使用if结构来模拟case语句。如果情况属实,那么您可以使用break 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;
            }
        }

答案 15 :(得分:1)

我认为有理由使用switch语句。如果您正在使用xText生成的Code。或者另一种EMF生成的类。

instance.getClass().getName();

返回类实现名称的String。即:     org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

返回简单的表示,即:     EcoreUtil

答案 16 :(得分:1)

这样可以更快地完成工作,以防万一 - 你有相对多的'案件' - 流程在性能敏感的上下文中执行

public static <T> T process(Object model) {
    switch (model.getClass().getSimpleName()) {
        case "Trade":
            return processTrade();
        case "InsuranceTransaction":
            return processInsuranceTransaction();
        case "CashTransaction":
            return processCashTransaction();
        case "CardTransaction":
            return processCardTransaction();
        case "TransferTransaction":
            return processTransferTransaction();
        case "ClientAccount":
            return processAccount();
        ...
        default:
            throw new IllegalArgumentException(model.getClass().getSimpleName());
    }
}

答案 17 :(得分:1)

不幸的是,由于switch-case语句需要一个常量表达式,因此不可能开箱即用。为了克服这个问题,一种方法是使用带有类名的枚举值,例如

public enum MyEnum {
   A(A.class.getName()), 
   B(B.class.getName()),
   C(C.class.getName());

private String refClassname;
private static final Map<String, MyEnum> ENUM_MAP;

MyEnum (String refClassname) {
    this.refClassname = refClassname;
}

static {
    Map<String, MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
        map.put(instance.refClassname, instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
}

public static MyEnum get(String name) {
    return ENUM_MAP.get(name);
 }
}

这样就可以使用这样的switch语句

MyEnumType type = MyEnum.get(clazz.getName());
switch (type) {
case A:
    ... // it's A class
case B:
    ... // it's B class
case C:
    ... // it's C class
}

答案 18 :(得分:1)

Java现在允许您以OP的方式进行切换。他们称其为Pattern Matching进行切换。目前正在草拟中,但是我看到他们最近在交换机中投入了多少工作,我认为它将完成。 JEP中给出的示例是

String formatted;
switch (obj) {
    case Integer i: formatted = String.format("int %d", i); break;
    case Byte b:    formatted = String.format("byte %d", b); break;
    case Long l:    formatted = String.format("long %d", l); break;
    case Double d:  formatted = String.format("double %f", d); break;
    case String s:  formatted = String.format("String %s", s); break
    default:        formatted = obj.toString();
}  

或使用其lambda语法并返回值

String formatted = 
    switch (obj) {
        case Integer i -> String.format("int %d", i)
        case Byte b    -> String.format("byte %d", b);
        case Long l    -> String.format("long %d", l); 
        case Double d  -> String.format("double %f", d); 
        case String s  -> String.format("String %s", s); 
        default        -> obj.toString();
    };

无论哪种方式,他们一直在用开关做一些很酷的事情。

答案 19 :(得分:1)

如果您想避免if(){} else if{}的冗长性,可以考虑将单个文件切换为kotlin,并将类似开关的when expressionis运算符结合使用

在任何情况下,Kotlin and java files can co-exist in a project

when (this) { //switch-like statement in kotlin supporting class-pattern-matching and smart casts via `is` operator.
    is A -> doA()
    is B -> doB()
    is C -> doC()
}

答案 20 :(得分:0)

用类名创建一个枚举

public enum ClassNameEnum {
    A, B, C
}

找到对象的类名。 在枚举上写一个 switch 案例。

private void switchByClassType(Object obj) {

        ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName());

        switch (className) {
            case A:
                doA();
                break;
            case B:
                doB();
                break;
            case C:
                doC();
                break;
        }
    }
}

希望这会有所帮助。

答案 21 :(得分:0)

这是使用http://www.vavr.io/

在Java 8中完成此功能的一种功能方式
import static io.vavr.API.*;
import static io.vavr.Predicates.instanceOf;
public Throwable liftRootCause(final Throwable throwable) {
        return Match(throwable).of(
                Case($(instanceOf(CompletionException.class)), Throwable::getCause),
                Case($(instanceOf(ExecutionException.class)), Throwable::getCause),
                Case($(), th -> th)
        );
    }

答案 22 :(得分:0)

Eclipse Modeling Framework有一个有趣的想法,它也考虑了继承。基本概念在Switch interface中定义:通过调用 doSwitch 方法来完成切换。

真正有趣的是实现。对于每种类型的兴趣,

public T caseXXXX(XXXX object);

方法必须实现(默认实现返回null)。 doSwitch 实现将尝试为其所有类型层次结构在对象上调用 caseXXX 方法。符合以下条件的

BaseType baseType = (BaseType)object;
T result = caseBaseType(eAttribute);
if (result == null) result = caseSuperType1(baseType);
if (result == null) result = caseSuperType2(baseType);
if (result == null) result = caseSuperType3(baseType);
if (result == null) result = caseSuperType4(baseType);
if (result == null) result = defaultCase(object);
return result;

实际的框架为每个类使用一个整数id,因此逻辑实际上是一个纯开关:

public T doSwitch(Object object) {
    return doSwitch(object.class(), eObject);
}

protected T doSwitch(Class clazz, Object object) {
    return doSwitch(getClassifierID(clazz), object);
}

protected T doSwitch(int classifierID, Object theObject) {
    switch (classifierID) {
    case MyClasses.BASETYPE:
    {
      BaseType baseType = (BaseType)object;
      ...
      return result;
    }
    case MyClasses.TYPE1:
    {
      ...
    }
  ...

您可以查看ECoreSwitch的完整实现,以获得更好的主意。

答案 23 :(得分:0)

虽然无法编写switch语句,但可以为每种给定类型扩展到特定处理。一种方法是使用标准的双重调度机制。我们要基于类型“切换”的示例是Jersey异常映射器,其中我们需要将大量异常映射到错误响应。虽然对于这种特定情况,可能有更好的方法(即使用将每个异常转换为错误响应的多态方法),但使用双调度机制仍然有用且实用。

interface Processable {
    <R> R process(final Processor<R> processor);
}

interface Processor<R> {
    R process(final A a);
    R process(final B b);
    R process(final C c);
    // for each type of Processable
    ...
}

class A implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class B implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class C implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

然后在需要“开关”的任何地方,您可以按以下步骤进行操作:

public class LogProcessor implements Processor<String> {
    private static final Logger log = Logger.for(LogProcessor.class);

    public void logIt(final Processable base) {
        log.info("Logging for type {}", process(base));
    }

    // Processor methods, these are basically the effective "case" statements
    String process(final A a) {
        return "Stringifying A";
    }

    String process(final B b) {
        return "Stringifying B";
    }

    String process(final C c) {
        return "Stringifying C";
    }
}