我有一个问题,使用开关情况的instanceof对象:
例如:我的问题可以在Java中重现:
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
如何使用switch…case实现它?
我有一个问题,使用开关情况的instanceof对象:
例如:我的问题可以在Java中重现:
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
如何使用switch…case实现它?
当前回答
创建一个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() );
}
这避免了重复,消除了几乎所有的分支语句,并简化了维护。
其他回答
如果你绝对不能编码到接口,那么你可以使用枚举作为中介:
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;
}
不,这是不可能的。然而,您可能想要做的是将多态性作为处理这类问题的一种方法。
如果您想避免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()
}
下面是在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)
);
}
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的完整实现,以获得更好的理解。