我有一个问题,使用开关情况的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实现它?
当前回答
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";
}
}
其他回答
创建一个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() );
}
这避免了重复,消除了几乎所有的分支语句,并简化了维护。
如果您可以操作公共接口,您可以在枚举中添加,并让每个类返回唯一的值。您不需要instanceof或visitor模式。
对我来说,逻辑需要写在switch语句中,而不是对象本身。这就是我的解决方案:
ClassA、ClassB和ClassC实现了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,你可以为枚举设置字符串值,并且switch case块仍然可以工作。
Java现在允许你以op的方式切换。他们称之为模式匹配切换。它在Java 17发布。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();
};
不管怎样,他们一直在用开关做很酷的东西。
还有一种更简单的方法来模拟使用instanceof的开关结构,你可以通过在方法中创建一个代码块并用标签命名它来实现。然后使用if结构来模拟case语句。如果情况为真,则使用断点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;
}
}
我知道这有点晚了,但对于未来的读者来说……
注意上面的方法,这些方法仅仅是基于A, B, C类的名称…:
除非你能保证A, B, C…(Base的所有子类或实现者)是final,然后A, B, C的子类…不会被处理。
即使if, elseif, elseif ..方法对于大量子类/实现者较慢,它更准确。