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

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

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

如何使用switch…case实现它?


当前回答

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

其他回答

Eclipse建模框架有一个有趣的想法,它也考虑了继承。Switch接口中定义了基本概念:通过调用doSwitch方法来完成切换。

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

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的完整实现,以获得更好的理解。

从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

正如前面的回答所讨论的,传统的面向对象方法是使用多态性而不是开关。对于这种技巧,甚至有一个经过良好记录的重构模式:用多态性替换Conditional。每当我使用这种方法时,我也喜欢实现一个Null对象来提供默认行为。

从Java 8开始,我们可以使用lambda和泛型来提供函数式程序员非常熟悉的东西:模式匹配。它不是语言的核心特性,但是VAVR库(以前是Javaslang库)提供了一个实现。文档中的例子:

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

它不是Java世界中最自然的范例,所以要谨慎使用。虽然泛型方法将使您不必对匹配的值进行类型转换,但我们缺少一种分解匹配对象的标准方法,例如Scala的case类。

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

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 8中使用http://www.vavr.io/来实现这一功能的方法

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