我有一个问题,为instanceof
对象使用switch case:
例如:我的问题可以用Java重现:
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
如何使用switch...case
实现?
这是子类型多态性有助于的典型场景。请执行下列操作
interface I {
void do();
}
class A implements I { void do() { doA() } ... }
class B implements I { void do() { doB() } ... }
class C implements I { void do() { doC() } ... }
然后你可以简单地在do()
上调用this
。
如果您无法自由更改A
,B
和C
,您可以应用访问者模式来实现相同目标。
你不能只使用byte,short,char,int,String和枚举类型(以及基元的对象版本,它也取决于你的java版本,字符串可以在java 7中使用switch
ed)
如果您可以操作公共接口,则可以添加枚举并让每个类返回唯一值。您不需要instanceof或访问者模式。
对我来说,逻辑需要在switch语句中写入,而不是对象本身。这是我的解决方案:
ClassA, ClassB, and ClassC implement 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 1.8代码:
mySwitch("YY")
.myCase("AA", (o) -> {
System.out.println(o+"aa");
})
.myCase("BB", (o) -> {
System.out.println(o+"bb");
})
.myCase("YY", (o) -> {
System.out.println(o+"yy");
})
.myCase("ZZ", (o) -> {
System.out.println(o+"zz");
});
将输出:
YYyy
示例代码使用字符串,但您可以使用任何对象类型,包括Class。例如.myCase(this.getClass(), (o) -> ...
需要以下代码段:
public Case mySwitch(Object reference) {
return new Case(reference);
}
public class Case {
private Object reference;
public Case(Object reference) {
this.reference = reference;
}
public Case myCase(Object b, OnMatchDo task) {
if (reference.equals(b)) {
task.task(reference);
}
return this;
}
}
public interface OnMatchDo {
public void task(Object o);
}
这个怎么样 ?
switch (this.name)
{
case "A":
doA();
break;
case "B":
doB();
break;
case "C":
doC();
break;
default:
console.log('Undefined instance');
}
我认为有理由使用switch语句。如果您正在使用xText生成的Code。或者另一种EMF生成的类。
instance.getClass().getName();
返回类实现名称的String。即:org.eclipse.emf.ecore.util.EcoreUtil
instance.getClass().getSimpleName();
返回简单的表示,即:EcoreUtil
如果你需要通过“this”对象的类类型“切换”,这个答案是最好的https://stackoverflow.com/a/5579385/2078368
但是如果你需要将“switch”应用于任何其他变量。我建议另一个解决方案。定义以下界面:
public interface ClassTypeInterface {
public String getType();
}
在要“切换”的每个类中实现此接口。例:
public class A extends Something implements ClassTypeInterface {
public final static String TYPE = "A";
@Override
public String getType() {
return TYPE;
}
}
之后,您可以通过以下方式使用它:
switch (var.getType()) {
case A.TYPE: {
break;
}
case B.TYPE: {
break;
}
...
}
您应该关心的唯一事项 - 在实现ClassTypeInterface的所有类中保持“类型”唯一。这不是一个大问题,因为在任何交集的情况下,您会收到“switch-case”语句的编译时错误。
有一种更简单的模拟使用instanceof的开关结构的方法,你可以通过在方法中创建一个代码块并用标签命名它来实现。然后使用if结构来模拟case语句。如果情况属实,那么您可以使用break 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;
}
}
不幸的是,由于switch-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语句
MyEnumType 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
}
这将更快地工作,并在案件中有意义 - 你有相对多的'案例' - 在性能敏感的上下文中执行进程
public static <T> T process(Object model) {
switch (model.getClass().getSimpleName()) {
case "Trade":
return processTrade();
case "InsuranceTransaction":
return processInsuranceTransaction();
case "CashTransaction":
return processCashTransaction();
case "CardTransaction":
return processCardTransaction();
case "TransferTransaction":
return processTransferTransaction();
case "ClientAccount":
return processAccount();
...
default:
throw new IllegalArgumentException(model.getClass().getSimpleName());
}
}
使用类名创建枚举。
public enum ClassNameEnum {
A, B, C
}
找到对象的类名称。在枚举上写一个开关盒。
private void switchByClassType(Object obj) {
ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName());
switch (className) {
case A:
doA();
break;
case B:
doB();
break;
case C:
doC();
break;
}
}
}
希望这可以帮助。
如果你绝对无法编码到接口,那么你可以使用枚举作为中介:
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;
}
这是使用http://www.vavr.io/在Java 8中完成它的功能方法
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 Modeling Framework有一个有趣的想法,也考虑了继承。基本概念在Switch interface中定义:通过调用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的完整实现以获得更好的主意。
虽然不可能编写switch语句,但可以分支到每个给定类型的特定处理。一种方法是使用标准的双重调度机制。我们想要根据类型“切换”的示例是Jersey异常映射器,我们需要将大量异常映射到错误响应。虽然对于这种特定情况,可能有更好的方法(即使用将每个异常转换为错误响应的多态方法),使用双调度机制仍然是有用和实用的。
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";
}
}
以防万一有人会阅读它:
Java中的最佳解决方案是:
public enum Action {
a{
void doAction(...){
// some code
}
},
b{
void doAction(...){
// some code
}
},
c{
void doAction(...){
// some code
}
};
abstract void doAction (...);
}
这种模式的好处是:
void someFunction ( Action action ) {
action.doAction(...);
}
注意:这种模式在Joshua的Bloch“Effective Java(第2版)”中有所描述。
只需创建一个Map,其中类是键,功能,即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());
//当然,重构一下只能初始化一次
doByClass.get(getClass()).run();
如果您需要检查异常,而不是实现抛出异常的FunctionalInterface并使用它而不是Runnable。
你不能。 switch
语句只能包含case
语句,这些语句是编译时常量并且计算为整数(最多Java 6和Java 7中的字符串)。
您正在寻找的是在函数式编程中称为“模式匹配”。
正如在顶部答案中所讨论的,传统的OOP方法是使用多态而不是切换。对于这个技巧,甚至有一个记录良好的重构模式:Replace Conditional with Polymorphism。每当我达到这种方法时,我也想实现一个Null object来提供默认行为。
从Java 8开始,我们可以使用lambdas和泛型来为我们提供一些函数式程序员非常熟悉的东西:模式匹配。它不是核心语言功能,但Javaslang library提供了一个实现。来自javadoc的示例:
Match.ofType(Number.class)
.caze((Integer i) -> i)
.caze((String s) -> new BigDecimal(s))
.orElse(() -> -1)
.apply(1.0d); // result: -1
它不是Java世界中最自然的范例,因此请谨慎使用它。虽然泛型方法将使您不必对匹配值进行类型转换,但我们缺少一种标准方法来分解匹配的对象,例如Scala's case classes。
我知道这已经很晚了,但对于未来的读者来说......
注意上面仅基于A,B,C ......类的名称的方法:
除非你能保证A,B,C ......(Base的所有子类或实现者)都是最终的,否则A,B,C ......的子类将不会被处理。
即使if,elseif,elseif ..方法对于大量子类/实现者来说速度较慢,但它更准确。
不,没有办法做到这一点。然而,您可能想要做的是将Polymorphism视为处理这类问题的一种方法。
像这样使用switch语句不是面向对象的方式。你应该使用多态的力量。简单写一下
this.do()
之前已经设置了基类:
abstract class Base {
abstract void do();
...
}
这是A
,B
和C
的基类:
class A extends Base {
void do() { this.doA() }
}
class B extends Base {
void do() { this.doB() }
}
class C extends Base {
void do() { this.doC() }
}