379

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

For example: my problem can be reproduced in Java as following-

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?

3
  • 7
    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. Commented Apr 7, 2011 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. Commented Jun 2, 2014 at 16:14
  • It is possible with the classname as an Enum value Commented Mar 27, 2019 at 15:26

31 Answers 31

273

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.

Sign up to request clarification or add additional context in comments.

11 Comments

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?
The last comment about the visitor pattern is wrong. You would still need to make A,B and C implement an interface.
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 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.
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
|
133

Java now allows you to switch in the manner of the OP. They call it Pattern Matching for switch. It was released as a preview feature of Java 17 (1) and is a full-fledged (non-preview) feature of Java 21(2). 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 the 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(); }; 

4 Comments

This is only in preview mode for Java 17.
@A_Arnold "This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future Java SE releases." ref: docs.oracle.com/en/java/javase/17/language/…
Java 21 is available, it's final now and no longer a preview feature.
btw, that is no lambda syntax, at most it looks like so - it is called switch rule and it could also be used in the first snippet (even it not being a switch expression): switch (obj) { case Integer i -> formatted = String.format("int %d", i); case Byte b -> formatted = ...
127

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

4 Comments

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.
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
usually people look toward switch due to performance considerations, and Enum.instanceOf should not be mentioned here.
This breaks automatic rename for class names in case A, case B and case C when refactoring with the most used IDEs.
81

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.

6 Comments

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...)
@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.
This only works for exact matches. Class<ArrayList> will not match Class<List>.
It's a good answer to the question, but I don't like the code. It's obscure, and I'd much rather prefer the initial code, albeit it can be put in a separate service you will rarely need to view / touch.
@html_programmer fair enough, but there is a fine line between "obscure" and "ignorant" especially regarding functional programming
|
41

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

8 Comments

nice! Is the @Override required above each implementation of doAction()?
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.
No, it will be done automatically at runtime. If you call someFunction(Action.a) then a.doAction will be called.
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.
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.
|
29

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

4 Comments

No, in most functional languages you can't pattern match on types, only on constructors. That is at least true in ML and Haskell. In Scala and OCaml it is possible but not the typical application of pattern matching.
Sure, but checking against constructors would be "equivalent" to the scenario described above.
In some cases, but not in general.
Switches can also support enums.
19

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.

Comments

14

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 } 

2 Comments

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.
Since the lookup Map is read-only and initialized by one thread only, a simple HashMap should be preferred from my point of view.
13

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 String TRADE = Trade.class.getName(); ... switch (model.getClass().getName()) { case TRADE: 

9 Comments

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
yeah, this is not
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)
@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.
I'd say instanceof should be avoided in many cases
|
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.

1 Comment

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.

Comments

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

1 Comment

@jmg suggeests (stackoverflow.com/questions/5579309/switch-instanceof/…) using an interface instead of an abstract base class. That can be superior in some circusmtances.
5

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)

3 Comments

You can't switch on Strings in Java 6. And you can't switch on "object versions of the primitives".
@Bozho I did say it depends on your java version, in Java 7 you can switch on Strings.
@Lukas Eder check your java spec you can
4

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

1 Comment

You are only switching on strings. This was about switching on different classes.
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.

1 Comment

The value field is redundant if you only want to distinguish the enum constants - you can use the constants directly (as you do).
3

It is totally possible in Java21.

Here is the example for the same

package org.example; import java.util.List; interface Message { } class TextMessage implements Message { } class ByteMessage implements Message { } class ObjectMessage implements Message { } class BooleanMessage implements Message { } public class SwitchClass { public static void main(String[] args) { List<Message> messageList = List.of( new TextMessage(), new ByteMessage(), new ObjectMessage(), new BooleanMessage() ); for (Message message : messageList) { switch (message) { case TextMessage textMessage-> { System.out.println(textMessage.getClass()); } case ByteMessage byteMessage-> { System.out.println(byteMessage.getClass()); } case ObjectMessage objectMessage-> { System.out.println(objectMessage.getClass()); } case BooleanMessage booleanMessage-> { System.out.println(booleanMessage.getClass()); } default -> { System.out.println("Unknown message type"); } } } } } 

1 Comment

Furthermore, if you use sealed classes then you no longer need the default!
2

How about this ?

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

4 Comments

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).
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.
Not a great idea. Class names are not unique if you have multiple class loaders.
Breaks when it comes to code compression (→ ProGuard)
2

Before we get stable pattern matching for switch statement, Java 16/17 could provide limited support with "Pattern Matching for instanceof":

if (req instanceof UserRequest userReq) { userReq.doUserStuff(); } else if (req instanceof AdminRequest adminReq) { adminReq.doAdminStuff(); } 

It frees you from extra casting statement.

See https://openjdk.org/jeps/394

Comments

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

1 Comment

You can't use it in switch as case condition because it is not constant value
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.

4 Comments

Instead of using String for the TYPE, you can use an enum and uniqueness is guaranteed (as done in this answer). However, with any of the approaches you will have to refactor in two places when you do a rename.
@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.
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 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.
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.

1 Comment

In contrast to this approach 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.
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) ); } 

Comments

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

1 Comment

This looks a lot like the Visitor pattern, which was already discussed in this answer: stackoverflow.com/a/5579385
1

Starting from Java 17 you can use pattern matching for switch expressions (preview feature) JEP-406.

public void doAction(Object o) { return switch (o) { case A a -> doA(a); case B b -> doB(b); case C c -> doC(c); default -> log.warn("Unrecognized type of {}", o); }; } 

Guard pattern is also available:

public void doAction(Object o) { return switch (o) { case String s && !s.isBlank() -> handle(s); }; } 

You need to enable preview features to use it: java --enable-preview

1 Comment

This was already answered.
1

In Java 19 recent release this has been very easy and useful : Create interface and have method called makesomenoise() which accepts animal parameter.

void makeSomeNoise (Animal animal) { switch (animal) { case Dog dog → dog.bark(); case Cat catcat.meow(); default throw new RuntimeException ("WTH is it???"); } } 

2 Comments

This was already answered.
posted code is not (valid) Java
1

In Java 21 you can use "Pattern Matching for Switch". See https://openjdk.org/jeps/441. This is no preview feature anymore.

Comments

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

2 Comments

How is this any better than the if... else if code given by the OP?
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.
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.

Comments

0

I think we can do it more naturally without any modifications like this:

switch (true) { case X instanceof A: // do something; break; case X instanceof B: // do something; break; default: // do something; break; } 

Hope it helps! :)

2 Comments

Doesn't work with Java 17. If it works on a newer version please mention it on the answer.
X instanceof A is not a valid case constant (nor null, neither an enum constant) as required by the Java Language Specification 14.11.1. Switch Blocks (Version 24)
0

For an even cleaner version, try combining two recent additions to Java

  • sealed classes

  • switching on instanceof

This allows you to drop the default case as the compiler can now exhaustively check that you have covered all cases.

sealed interface Effect permits Shout, Bark { } record Shout(String value) implements Effect { } record Bark(int times) implements Effect {} 

Which can be switched on like this

switch(effect) { case Shout s -> System.out.println(s.value.toUpperCase()); case Bark b -> { for(int i = 0; i < b.times; i++) System.out.println("Whooof!"); } } 

Works great for approaches relying on Functional Core, Imperative Shell. A fully runnable example is in this Gist

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.