296

I have a question of using switch case for instanceof object:

For example: my problem can be reproduced in Java:

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

How would it be implemented using switch...case?

Murat Karagöz
  • 26,294
  • 9
  • 70
  • 97
olidev
  • 17,514
  • 49
  • 124
  • 187
  • 6
    If you really feel you need a switch you could hash the class name to an int and use that, watch out for possible clashes though. Adding as comment rather than an answer as I don't like the idea of this actually been used. Maybe what you really need is the visitor pattern. – vickirk Apr 07 '11 at 10:13
  • 1
    As of java 7 you could even switch on the fully qualified class name to avoid such hash clashes as @vickirk pointed out, but it's still ugly. – Mitja Jun 02 '14 at 16:14
  • It is possible with [the classname as an Enum value](https://stackoverflow.com/a/55380782/4467208) – Murat Karagöz Mar 27 '19 at 15:26

24 Answers24

241

This is a typical scenario where subtype polymorphism helps. Do the following

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() } ... }

Then you can simply call do() on this.

If you are not free to change A, B, and C, you could apply the visitor pattern to achieve the same.

jmg
  • 6,817
  • 1
  • 16
  • 21
  • 36
    Visitor pattern means that A,B and C have to implement an interface with an abstract method that takes a Visitor as an input parameter, what if you cannot change A,B,C and none of them implements that interface? – thermz Aug 07 '14 at 10:11
  • 23
    The last comment about the visitor pattern is wrong. You would still need to make A,B and C implement an interface. – Ben Thurley Jan 27 '15 at 17:17
  • 1
    @BenThurley You're technically correct. Yet, there is a common (at least for me) modification of the visitor pattern, where you encapsulate the dynamic dispatch into a dispatcher class which contains OP's instanceof cascade. But then, this cascade is at least reusable and separately testable for correctness and completeness (via reflection based unit tests). And you don't have to change the original classes. – jmg Jan 30 '15 at 13:12
  • 11
    Sadly this does not work if the do()-Code requires the environment of the host (i.e. access to variables not present in the do() itself). – mafu May 23 '15 at 16:47
  • 2
    @mafu OP's question was about type based dispatching. If your do() method needs more input in order to dispatch than your problem is IMHO outside the scope of the question discussed here. – jmg May 29 '15 at 16:16
  • 5
    this answer assumes that you can modify the classes A,B,C, while I think the point is how to do that without modify A,B,C because they might be in a third part library – cloudy_weather Nov 29 '16 at 10:53
  • 2
    Quite late, but like daniele said: yes, this method is useless for checking types in a generic collection for example, where you have a set of objects which you know, can be of some set of types, but you must check which of them it is. – Megakoresh Mar 15 '17 at 12:40
  • Downvoted because you cannot use the visitor pattern without modifying the classes A, B, and C. – gatinueta Apr 14 '19 at 12:38
  • If you cannot or don't want to modify A,B,C, you can try using the Decorator pattern to add the required method to those classes and provide the "Element" interface for the Visitor pattern. – FraK Jul 28 '20 at 17:39
108

if you absolutely cannot code to an interface, then you could use an enum as an intermediary:

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;

}
Nico
  • 1,806
  • 2
  • 13
  • 18
  • thx, I also had to make some changes: 1) initialize each enum id with the Class reference; 2) assert the class simple name with the enum id .toString(); 3)find the enum thru the stored Class reference per enum id. I think this is also obfuscation safe then. – Aquarius Power Feb 02 '15 at 19:05
  • if this.getClass().getSimpleName() does not match a value of CLAZZ it throwns an Exception... it's better to surround with a try catch block and the Exception would be treated as the "default" or "else" option of the switch – tetri Feb 05 '15 at 10:31
  • usually people look toward switch due to performance considerations, and Enum.instanceOf [should not be mentioned here](https://medium.com/javarevisited/micro-optimizations-in-java-good-nice-and-slow-enum-261e6f77bd2e). – Mike Feb 10 '21 at 21:54
51

Create a Map where the key is Class<?> and the value is an expression (lambda or similar). Consider:

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

If you need checked exceptions than implement a FunctionalInterface that throws the Exception and use that instead of Runnable.


Here's a real-word before-and-after showing how this approach can simplify code.

The code before refactoring to a map:

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

The code after refactoring to a map:

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

This avoids repetition, eliminates nearly all branching statements, and simplifies maintenance.

Dave Jarvis
  • 28,853
  • 37
  • 164
  • 291
Novaterata
  • 3,327
  • 22
  • 39
  • 1
    Upvoted; this is one of the few answers that actually helps the OP do what he is asking for (and yes, it's often possible to refactor the code to not have to do `instanceof`, and no, my scenario is unfortunately not one of those that easily fits into that box...) – Per Lundberg Jul 11 '19 at 09:00
  • @SergioGutiérrez Thanks. Well, this pattern should only be needed with an external library. And even then you can just create an interface with an adapter implementation instead, but it's useful where you want the behavioral DIFF, so to speak, to be more obvious. Similar to fluent vs annotation API routing I suppose. – Novaterata Nov 11 '19 at 14:21
39

Just in case if someone will read it:

The BEST solution in java is :

public enum Action { 
    a{
        void doAction(...){
            // some code
        }

    }, 
    b{
        void doAction(...){
            // some code
        }

    }, 
    c{
        void doAction(...){
            // some code
        }

    };

    abstract void doAction (...);
}

The GREAT benefits of such pattern are:

  1. You just do it like (NO switches at all):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
    
  2. In case if you add new Action called "d" you MUST imlement doAction(...) method

NOTE: This pattern is described in Joshua's Bloch "Effective Java (2nd Edition)"

se.solovyev
  • 1,863
  • 1
  • 16
  • 20
  • 1
    nice! Is the `@Override` required above each implementation of `doAction()`? – mateuscb Apr 23 '14 at 21:34
  • 13
    How is this the "BEST" solution? How would you decide which `action` to use? By an outer instanceof-cascade that calls `someFunction()` with the correct `action`? This just adds another level of indirection. – PureSpider Jan 14 '16 at 13:30
  • 1
    No, it will be done automatically at runtime. If you call someFunction(Action.a) then a.doAction will be called. – se.solovyev Jan 15 '16 at 14:15
  • 12
    I don't understand this. How would you know which enum to use? As @PureSpider said, this seems like just another level of work to do. – James Manes Jan 27 '16 at 14:59
  • 1
    It is probably case, when you have enum inside appropriate entity already, then you can take it from there... – Andrii Plotnikov Sep 27 '16 at 08:14
  • Thank you! following yout sugestion, here what I made: 1- Add the enom at the Super Class: 2- Add a abstract getter in Super Class to get the enom; 3- Implemented the abstract enom getter um concrete Class; 4- finished runing the concrete class method. – mizerablebr Feb 10 '17 at 14:29
  • This is a very handy, concise way to implement the Abstract Factory Pattern. Thank you! – Joe Coder Apr 06 '18 at 14:43
  • 2
    It's very sad that you **did not have offered a complete example**, e.g. how map any Class-Instance of a,b or C to this enum. I'll try to cast the Instance to this Enum. – Tom Jul 28 '19 at 19:16
25

You can't. The switch statement can only contain case statements which are compile time constants and which evaluate to an integer (Up to Java 6 and a string in Java 7).

What you are looking for is called "pattern matching" in functional programming.

See also Avoiding instanceof in Java

Community
  • 1
  • 1
Carlo V. Dango
  • 11,816
  • 15
  • 63
  • 107
20

As discussed in the top answers, the traditional OOP approach is to use polymorphism instead of switch. There is even a well documented refactoring pattern for this trick: Replace Conditional with Polymorphism. Whenever I reach for this approach, I like to also implement a Null object to provide the default behaviour.

Starting with Java 8, we can use lambdas and generics to give us something functional programmers are very familiar with: pattern matching. It's not a core language feature but the VAVR Library - formerly Javaslang library provides one implementation. Example from the docs:

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

It's not the most natural paradigm in the Java world so use it with caution. While the generic methods will save you from having to typecast the matched value, we're missing a standard way to decompose the matched object as with Scala's case classes for example.

Alex R
  • 10,320
  • 12
  • 76
  • 145
Pavel
  • 3,303
  • 24
  • 31
11

Unfortunately, it is not possible out of the box since the switch-case statement expects a constant expression. To overcome this, one way would be to use enum values with the class names e.g.

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

With that is is possible to use the switch statement like this

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
}
Murat Karagöz
  • 26,294
  • 9
  • 70
  • 97
  • 1
    I believe that until JEP issue 8213076 is fully implemented, this is the cleanest way get a switch statement based on the type, without modifying the target class. – Rik Schaaf May 17 '20 at 14:54
10

I know this is very late but for future readers ...

Beware of the approaches above that are based only on the name of the class of A, B, C ... :

Unless you can guarantee that A, B, C ... (all subclasses or implementers of Base) are final then subclasses of A, B, C ... will not be dealt with.

Even though the if, elseif, elseif .. approach is slower for large number of subclasses/implementers, it is more accurate.

JohnK
  • 179
  • 1
  • 9
  • Indeed, you [should never use the polymorphimsm](http://valjok.blogspot.com/2013/01/java-oop-is-sux-from-very-beginning.html) (aka OOP) – Val Mar 02 '14 at 13:16
8

java 7+

public <T> T process(Object model) {
    switch (model.getClass().getSimpleName()) {
    case "Trade":
        return processTrade((Trade) model);
    case "InsuranceTransaction":
        return processInsuranceTransaction((InsuranceTransaction) model);
    case "CashTransaction":
        return processCashTransaction((CashTransaction) model);
    case "CardTransaction":
        return processCardTransaction((CardTransaction) model);
    case "TransferTransaction":
        return processTransferTransaction((TransferTransaction) model);
    case "ClientAccount":
        return processAccount((ClientAccount) model);
    ...
    default:
        throw new IllegalArgumentException(model.getClass().getSimpleName());
    }
}

You can be even faster by for omitting string manipulation inside getSimpleName by for introducing constants and using full class name:

public static final TRADE = Trade.class.getName();
...
switch (model.getClass().getName()) {
case TRADE:
Mike
  • 17,033
  • 22
  • 85
  • 113
  • 3
    This is not the same as doing an instanceof, as this only works if the implementation class is used to switch, but it will not work for interfaces/abstractclass/superclasses – lifesoordinary Apr 25 '19 at 14:25
  • yeah, this is not – Mike Apr 26 '19 at 08:17
  • A for effort, but aside from @lifesoordinary's comment, you also miss the typesafety that you normally have, because this answer uses hardcoded strings, instead of class references. It is quite easy to make a typo, especially if you'd need to extend this functionality with full canonical names if here was any overlap in class names with different package names.Edit: fixed typo (which kinda proves my point) – Rik Schaaf May 17 '20 at 14:59
  • @RikSchaaf in addition to losing type safety you also lose the possibility of using subclass implementations in the switch. I'd say that this should be avoided. – joshpetit Dec 03 '20 at 18:03
  • I'd say instanceof should be avoided in many [cases](https://stackoverflow.com/questions/596462/any-reason-to-prefer-getclass-over-instanceof-when-generating-equals) – Mike Dec 10 '20 at 12:30
  • @Mike agreed for the case of `equals()` implementations, to keep it symmetric. Also in a lot of cases you can rewrite your code to use polymorphism, instead of `instanceof`. There are still some valid use cases though. – Rik Schaaf Feb 10 '21 at 11:52
  • @RikSchaaf, your typo will be revealed by a test, you write unit tests, don't you? Also if you can rewrite you case with polymorphism, then it was 'polymorphism case', which has nothing to do with the question asked. Open `java.awt.Component#process...` methods and show me where you can put your polymorphism there. I personally used hash maps for similar cases, but the question was asked about switch, and I believe there could be performance reasons for that, because even hash map is fast, switch is [faster](https://stackoverflow.com/questions/27993819/hashmap-vs-switch-statement-performance) – Mike Feb 10 '21 at 21:45
  • Enum.valueOf seems to be much [worse](https://medium.com/javarevisited/micro-optimizations-in-java-good-nice-and-slow-enum-261e6f77bd2e) from performance point of view and should not be mentioned inline with `switch` and `HashMap` – Mike Feb 10 '21 at 22:02
7

Java now allows you to switch in the manner of the OP. They call it Pattern Matching for switch. It is currently under Draft but seeing how much work they have been putting into switches recently I think it will go through. The example given in the JEP is

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

or using their lambda syntax and returning a value

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

either way they've been doing cool stuff with switches.

A_Arnold
  • 2,081
  • 17
  • 36
6

Nope, there is no way to do this. What you might want to do is however to consider Polymorphism as a way to handle these kind of problems.

Andreas Johansson
  • 1,113
  • 5
  • 22
5

I personally like the following Java 1.8 code:

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

Will output:

YYyy

The sample code uses Strings but you can use any object type, including Class. e.g. .myCase(this.getClass(), (o) -> ...

Needs the following snippet:

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);
}
Feiteira
  • 761
  • 7
  • 9
5

Using switch statements like this is not the object oriented way. You should instead use the power of polymorphism. Simply write

this.do()

Having previously set up a base class:

abstract class Base {
   abstract void do();
   ...
}

which is the base class for A, B and 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() }
}
Raedwald
  • 40,290
  • 35
  • 127
  • 207
  • @jmg suggeests (http://stackoverflow.com/questions/5579309/switch-instanceof/5579385#5579385) using an interface instead of an abstract base class. That can be superior in some circusmtances. – Raedwald Apr 07 '11 at 10:15
4

You can't a switch only works with the byte, short, char, int, String and enumerated types (and the object versions of the primitives, it also depends on your java version, Strings can be switched on in java 7)

mark-cs
  • 4,579
  • 21
  • 31
3

If you can manipulate the common interface, you could do add in an enum and have each class return a unique value. You won't need instanceof or a visitor pattern.

For me, the logic needed to be in the written in the switch statement, not the object itself. This was my solution:

ClassA, ClassB, and ClassC implement CommonClass

Interface:

public interface CommonClass {
   MyEnum getEnumType();
}

Enum:

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

If you are on java 7, you can put string values for the enum and the switch case block will still work.

Gaʀʀʏ
  • 3,952
  • 2
  • 34
  • 56
  • The `value` field is redundant if you only want to distinguish the enum constants - you can use the constants directly (as you do). – user905686 Jul 01 '18 at 12:47
2

How about this ?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}
Saro Taşciyan
  • 5,034
  • 5
  • 27
  • 48
Joeri
  • 45
  • 2
  • 3
    Should point out that this works only on Java 7. And that you have to call `this.getSimpleName()` Not sure if the poster is confused with JS (yeah, he's using console, hehe). – pablisco Feb 15 '14 at 22:44
  • 5
    This has a problem of falling out of source code referential transparency. That is, your IDE won't be able to mantain the reference integrity. Suppose you want to rename your name. The reflection is evil. – Val Mar 02 '14 at 14:05
  • 1
    Not a great idea. Class names are not unique if you have multiple class loaders. – Doradus Nov 03 '15 at 00:30
  • Breaks when it comes to code compression (→ ProGuard) – Matthias Ronge Nov 23 '16 at 11:38
1

I think there are reasons to use a switch statement. If you are using xText generated Code perhaps. Or another kind of EMF generated classes.

instance.getClass().getName();

returns a String of the Class Implementation Name. i.e: org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

returns the simple represenation i.e: EcoreUtil

BeToken
  • 11
  • 3
1

If you need to "switch" thru the class type of "this" object, this answer is the best https://stackoverflow.com/a/5579385/2078368

But if you need to apply "switch" to any other variable. I would suggest another solution. Define following interface:

public interface ClassTypeInterface {
    public String getType();
}

Implement this interface in every class you want to "switch". Example:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

    @Override
    public String getType() {
        return TYPE;
    }
}

After that you can use it in following way:

switch (var.getType()) {
    case A.TYPE: {
        break;
    }
    case B.TYPE: {
        break;
    }
    ...
}

The only thing you should care about - keep the "types" unique across all the classes implementing the ClassTypeInterface. It's not a big problem, because in case of any intersection you receive a compile-time error for the "switch-case" statement.

Community
  • 1
  • 1
  • Instead of using String for the `TYPE`, you can use an enum and uniqueness is guaranteed (as done in [this answer](https://stackoverflow.com/a/19252352/905686)). However, with any of the approaches you will have to refactor in two places when you do a rename. – user905686 Jul 01 '18 at 12:51
  • @user905686 rename of what? In current example the type "A" is defined inside the Something class to minimize quantity of code. But in real life you obviously should define it outside (in some common place) and there are no any problem with further refactoring. – Sergey Krivenkov Jul 02 '18 at 15:23
  • I mean renaming the class A. Automatic refactoring might not include the variable `TYPE = "A"` when renaming. Especially if it is outside the corresponding class, one might also forget it when doing it manually. IntelliJ actually also finds the occurrences of the class name in strings or comments but that's just a text search (instead of looking at the syntax tree) and thus includes false positives. – user905686 Jul 04 '18 at 15:52
  • @user905686 it's just an example, to visualize the idea. Do not use String for type definitions in real project, declare some MyTypes class holder with integer constants (or enum) and use them in the classes implementing ClassTypeInterface. – Sergey Krivenkov Jul 08 '18 at 15:48
1

Create an Enum with Class names.

public enum ClassNameEnum {
    A, B, C
}

Find the Class name of the object. Write a switch case over the enum.

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

Hope this helps.

Siva Kumar
  • 530
  • 6
  • 11
  • 3
    In contrast to [this approach](https://stackoverflow.com/a/19252352/905686) where the coupling between enum constants and classes is done explicitly, you do the coupling implicitly by class name. This will break your code when you rename only one of the enum constant or the class whereas the other approach would still work. – user905686 Jul 01 '18 at 12:57
1

Here's a functional way of accomplishing it in Java 8 using 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)
        );
    }
Viswanath
  • 881
  • 7
  • 20
1

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

Then where ever the "switch" is needed, you can do it as follows:

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";
    }
}
Ravi Sanwal
  • 447
  • 3
  • 12
  • This looks a lot like the Visitor pattern, which was already discussed in this answer: https://stackoverflow.com/a/5579385 – typeracer Oct 24 '19 at 06:28
1

If you want to avoid the verbosity of if(){} else if{}, you may consider switching this single file to kotlin and use the switch-like when expression in combination with is operator.

In any case Kotlin and java files can co-exist in a project.

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()
}
Marinos An
  • 6,191
  • 2
  • 35
  • 68
0

there is an even simpler way of emulating a switch structure that uses instanceof, you do this by creating a code block in your method and naming it with a label. Then you use if structures to emulate the case statements. If a case is true then you use the break LABEL_NAME to get out of your makeshift switch structure.

        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;
            }
        }
Maurice
  • 4,056
  • 5
  • 30
  • 66
  • How is this any better than the `if`... `else if` code given by the OP? – typeracer Oct 24 '19 at 06:33
  • Just to elaborate on my previous comment: what you're proposing is essentially to replace `if`... `else if` with "goto" statements, which is the *wrong* way to implement control flow in languages like Java. – typeracer Oct 24 '19 at 06:45
0

The Eclipse Modelling Framework has an interesting idea that also considers inheritance. The basic concept is defined in the Switch interface: switching is done by invoking the doSwitch method.

What is really interesting is the implementation. For each type of interest, a

public T caseXXXX(XXXX object);

method must be implemented (with a default implementation returning null). The doSwitch implementation will attempt to call al the caseXXX methods on the object for all its type hierarchy. Something in the lines of:

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;

The actual framework uses an integer id for each class, so the logic is actually a pure switch:

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:
    {
      ...
    }
  ...

You can look at a complete implementation of the ECoreSwitch to get a better idea.

Arcanefoam
  • 490
  • 4
  • 19