Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch over type in java

Before I start, I know there are a bunch of answers to this question that suggest alternate approaches. I'm looking for assistance to this particular approach as to whether it is possible, and if not, similar approaches that might work.

I have a method that takes a superclass and calls a method based on the type of the passed object. for instance:

public void handle(Object o){   if (o instanceof A)     handleA((A)o);   else if (o instanceof B)     handleB((B)o);   else if (o instanceof C)     handleC((C)o);   else      handleUnknown(o); 

I can't modify the subtypes to override a handle() method, as this answer would suggest, because I don't own the classes. So the instanceof method is all I have.

I'd like to use a switch statement instead of if/else, simply because it's much neater. I know you can only switch on primitives and Strings, so I'm switching over the class name:

switch(o.getClass().getCanonicalName()){ case "my.package.A":   handleA((A)o);   break; case "my.package.B":   handleB((B)o);   break; case "my.package.C":   handleC((C)o);   break; default:   handleUnknown(o);   break; } 

The catch here is that the canonical names are VERY long (like 12 subpackages), and I can't call ClassName.class.getCanonicalName() in the case statement because Java doesn't allow that. So my next solution was an Enum. This is where I hit my problem.

I'd like my code to look something like this:

public enum Classes {   A (A.getCanonicalName()),   B (B.getCanonicalName()),   C (C.getCanonicalName()); }  switch (o.getClass().getCanonicalName()){ case Classes.A:   handleA((A)o);   break; case Classes.B:   handleB((B)o);   break; case Classes.C:   handleC((C)o);   break; default:   handleUnknown(o);   break; } 

But this doesn't compile. I'm not sure why. I'd like some approach that allows me to switch over the type without having to type out the entire canonical name. If I do that, I might as well just use if/else and instanceof.

NOTE There are a couple of types that have the same name (inner classes), so getSimpleName() is out.

like image 440
ewok Avatar asked Apr 10 '15 21:04

ewok


People also ask

What is switch () in Java?

The switch statement or switch case in java is a multi-way branch statement. Based on the value of the expression given, different parts of code can be executed quickly. The given expression can be of a primitive data type such as int, char, short, byte, and char.

Which type of data is accepted by switch statement in Java?

A switch works with the byte , short , char , and int primitive data types.

Which datatype is not allowed in switch in Java?

It doesn't allow variables. The case values must be unique. In case of duplicate value, it renders compile-time error. The Java switch expression must be of byte, short, int, long (with its Wrapper type), enums and string.

How do you return a switch in Java?

You can also think of the -> as a return statement which returns a value from the switch expression, and thus breaks the execution of the switch expression. The Java switch expression also works with Java String values.


2 Answers

Here is an approach that does not deal with class names at all, and dispatches as fast as a switch statement does: make a hash map to map Class<T> objects to class-specific handlers, and use the map instead of a switch:

// Declare an interface for your polymorphic handlers to implement. // There will be only anonymous implementations of this interface. private interface Handler {     void handle(Object o); } // Make a map that translates a Class object to a Handler private static final Map<Class,Handler> dispatch = new HashMap<Class,Handler>(); // Populate the map in a static initializer static {     dispatch.put(A.class, new Handler() {         public void handle(Object o) {             handleA((A)o);         }     });     dispatch.put(B.class, new Handler() {         public void handle(Object o) {             handleB((B)o);         }     });     dispatch.put(C.class, new Handler() {         public void handle(Object o) {             handleC((C)o);         }     }); } // This object performs the dispatch by looking up a handler, // and calling it if it's available private static void handle(Object o) {     Handler h = dispatch.get(o.getClass());     if (h == null) {         // Throw an exception: unknown type     }     h.handle(o); // <<== Here is the magic } 
like image 106
Sergey Kalinichenko Avatar answered Sep 22 '22 15:09

Sergey Kalinichenko


Using java 8 lambdas you can get to something like this:

Collection col = Arrays.asList(1,2,3); switchType(col,         caze(Collection.class, c->System.out.println(c.size())),        caze(ArrayBlockingQueue.class, bq->System.out.println(bq.remainingCapacity())),        caze(Queue.class, q->System.out.println(q.poll())),        caze(String.class, s->System.out.println(s.substring(0))),        caze(ArrayList.class, al->System.out.println(al.get(0))) ); 

In order to do that you should define the following static methods:

public static <T> void switchType(Object o, Consumer... a) {     for (Consumer consumer : a)         consumer.accept(o); }  public static <T> Consumer caze(Class<T> cls, Consumer<T> c) {     return obj -> Optional.of(obj).filter(cls::isInstance).map(cls::cast).ifPresent(c); }     
like image 25
eitan Avatar answered Sep 21 '22 15:09

eitan