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

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

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

如何使用switch…case实现它?


当前回答

我认为使用switch语句是有原因的。如果你使用xText生成的代码。或者另一种EMF生成的类。

instance.getClass().getName();

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

instance.getClass().getSimpleName();

返回简单的表示形式,即: EcoreUtil

其他回答

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

在函数式编程中,您要寻找的是所谓的“模式匹配”。

参见Java中避免instanceof

创建一个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() );
}

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

这个怎么样?

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

While it is not possible to write a switch statement, it is possible to branch out to specific processing for each given type. One way of doing this is to use standard double dispatch mechanism. An example where we want to "switch" based on type is Jersey Exception mapper where we need to map multitude of exceptions to error responses. While for this specific case there is probably a better way (i.e. using a polymorphic method that translates each exception to an error response), using double dispatch mechanism is still useful and practical.

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

不幸的是,这是不可能开箱即用的,因为开关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语句

MyEnum 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
}