我有一个问题,使用开关情况的instanceof对象:

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

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

如何使用switch…case实现它?


当前回答

从Java 17开始,您可以对开关表达式(预览特性)JEP-406使用模式匹配。

public void doAction(Object o) {
    return switch (o) {
        case A a -> doA(a);
        case B b -> doB(b);
        case C c -> doC(c);
        default -> log.warn("Unrecognized type of {}", o);
    };
}

防护模式也可用:

public void doAction(Object o) {
    return switch (o) {
        case String s && !s.isBlank() -> handle(s);
    };
}

你需要启用预览功能来使用它:java——enable-preview

其他回答

你不能只使用字节,short, char, int, String和枚举类型(以及原语的对象版本,这也取决于你的java版本,字符串可以在java 7中切换)

如果你绝对不能编码到接口,那么你可以使用枚举作为中介:

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;

}

创建一个Map,其中键为Class<?>,值为表达式(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());

// of course, refactor this to only initialize once

doByClass.get(getClass()).run();

如果你需要检查异常,那就实现一个抛出异常的FunctionalInterface,而不是使用Runnable。


下面是一个真实的单词前后对比,展示了这种方法如何简化代码。

重构映射之前的代码:

private Object unmarshall(
  final Property<?> property, final Object configValue ) {
  final Object result;
  final String value = configValue.toString();

  if( property instanceof SimpleDoubleProperty ) {
    result = Double.parseDouble( value );
  }
  else if( property instanceof SimpleFloatProperty ) {
    result = Float.parseFloat( value );
  }
  else if( property instanceof SimpleBooleanProperty ) {
    result = Boolean.parseBoolean( value );
  }
  else if( property instanceof SimpleFileProperty ) {
    result = new File( value );
  }
  else {
    result = value;
  }

  return result;
}

重构为映射后的代码:

private final Map<Class<?>, Function<String, Object>> UNMARSHALL = 
Map.of(
  SimpleBooleanProperty.class, Boolean::parseBoolean,
  SimpleDoubleProperty.class, Double::parseDouble,
  SimpleFloatProperty.class, Float::parseFloat,
  SimpleFileProperty.class, File::new
);

private Object unmarshall(
  final Property<?> property, final Object configValue ) {
  return UNMARSHALL
    .getOrDefault( property.getClass(), ( v ) -> v )
    .apply( configValue.toString() );
}

这避免了重复,消除了几乎所有的分支语句,并简化了维护。

如果您想避免If (){} else If{}的冗长,您可以考虑将这个文件切换到kotlin,并将类似于开关的when表达式与is操作符结合使用。

在任何情况下,Kotlin和java文件都可以共存于一个项目中,并生成一个可以在JVM中运行的jar。

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

我知道这有点晚了,但对于未来的读者来说……

注意上面的方法,这些方法仅仅是基于A, B, C类的名称…:

除非你能保证A, B, C…(Base的所有子类或实现者)是final,然后A, B, C的子类…不会被处理。

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