with switch multiple classes java types enums switch-statement instanceof

switch - Cambiar el tipo en java



switch by class (7)

Aquí hay un ejemplo de esto que usa un objeto simple para cada caso.

package mcve.util; import java.util.*; import java.util.function.*; /** * Allows switch-like statements with classes and consumers. */ public final class ClassSwitch implements Consumer<Object> { /** * For each of the specified cases, in order of their * appearance in the array, if cases[i].test(obj) returns * true, then invoke cases[i].accept(obj) and return. * * @param obj the object to switch upon * @param cases the cases for the switch * @throws NullPointerException * if any of the cases are null */ public static void cswitch(Object obj, Case<?>... cases) { if (cases != null) { for (Case<?> c : cases) { if (c.test(obj)) { c.accept(obj); break; } } } } /** * @param type the type of the case * @param action the action to perform * @param <T> the type of the case * @throws NullPointerException * if the type or action is null * @return a new Case */ public static <T> Case<T> ccase(Class<T> type, Consumer<? super T> action) { return new Case<>(type, action); } /** * @param <T> the type of the case */ public static final class Case<T> implements Predicate<Object>, Consumer<Object> { private final Class<T> type; private final Consumer<? super T> action; /** * @param type the type of the case * @param action the action to perform * @throws NullPointerException * if the type or action is null */ public Case(Class<T> type, Consumer<? super T> action) { this.type = Objects.requireNonNull(type, "type"); this.action = Objects.requireNonNull(action, "action"); } /** * @param obj the object to test * @return true if the object is an instance of T, else false */ @Override public boolean test(Object obj) { return type.isInstance(obj); } /** * @param obj the object to perform the action on * @throws ClassCastException * if the object is not an instance of T */ @Override public void accept(Object obj) { action.accept(type.cast(obj)); } } /** * An unmodifiable list of the cases in this switch. */ private final List<Case<?>> cases; /** * @param cases the cases for this switch * @throws NullPointerException * if any of the cases are null */ public ClassSwitch(Case<?>... cases) { if (cases == null) { this.cases = Collections.emptyList(); } else { List<Case<?>> list = new ArrayList<>(cases.length); for (Case<?> c : cases) { list.add(Objects.requireNonNull(c, "case")); } this.cases = Collections.unmodifiableList(list); } } /** * @return an unmodifiable view of the cases in this switch */ public List<Case<?>> getCases() { return cases; } /** * For each of the cases in this switch, in order of their * appearance in the list, if cases.get(i).test(obj) returns * true, then invoke cases.get(i).accept(obj) and return. * * @param obj the object to switch upon */ @Override public void accept(Object obj) { for (Case<?> c : cases) { if (c.test(obj)) { c.accept(obj); break; } } } }

Un ejemplo de uso sería algo como esto, asumiendo importaciones de, por ejemplo, import static mcve.util.ClassSwitch.*; :

cswitch(anObject, ccase(Byte.class, b -> System.out.println("Byte")), ccase(Short.class, s -> System.out.println("Short")), ccase(Integer.class, i -> System.out.println("Integer")), ccase(Long.class, l -> System.out.println("Long")), ccase(Float.class, f -> System.out.println("Float")), ccase(Double.class, d -> System.out.println("Double")) );

También puedes crear un objeto reutilizable:

ClassSwitch ts = new ClassSwitch(ccase(String.class, System.out::println), ccase(Double.class, System.out::println)); ts.accept(anObject);

Notas:

  • Si desea un caso default , puede usar Object.class como el último caso.

  • No hay forma de hacer un caso que se maneje null , pero podría modificarse un poco para eso. Podría, por ejemplo, hacer una class NullCase cuyo método de test devuelva obj == null .

Lo que también podría hacer es generar sobrecargas en lugar de usar varargs. Esto le permite asociar clases con consumidores utilizando solo declaraciones de métodos genéricos. El siguiente es un ejemplo bastante simple de esto:

package mcve.util; import java.util.*; import java.util.function.*; /** * Allows switch-like statements with classes and consumers. */ public final class GeneratedClassSwitch { private GeneratedClassSwitch() {} /** * Generates overloads for a class switch to System.out. * * For example, if max=4, then 5 methods are generated: * with 0, 1, 2, 3, and 4 cases. * * @param max * the number of cases in the largest overload generated * @param indents * the number of indents to indent each generated method * @throws IllegalArgumentException * if max is negative or greater than 26, or if indents * is negative */ public static void generateFixedOverloads(int max, int indents) { if (max < 0 || max > 26) { throw new IllegalArgumentException("max=" + max); } String indent = String.join("", Collections.nCopies(indents, " ")); for (int i = 0; i <= max; ++i) { System.out.print(indent); System.out.print("public static "); if (i > 0) { System.out.print("<"); for (char ch = ''A''; ch < ''A'' + i; ++ch) { if (ch != ''A'') { System.out.print(", "); } System.out.print(ch); } System.out.print("> "); } System.out.print("void cswitch"); if (i > 0) { System.out.println(); System.out.print(indent + " (Object o, "); for (char ch = ''A''; ch < ''A'' + i; ++ch) { if (ch != ''A'') { System.out.println(","); System.out.print(indent + " "); } System.out.print("Class<" + ch + "> class" + ch); System.out.print(", Consumer<? super " + ch + "> action" + ch); } } else { System.out.print("(Object o"); } System.out.println(") {"); for (char ch = ''A''; ch < ''A'' + i; ++ch) { if (ch == ''A'') { System.out.print(indent + " "); } else { System.out.print(" else "); } System.out.println("if (class" + ch + ".isInstance(o)) {"); System.out.print(indent + " "); System.out.println("action" + ch + ".accept(class" + ch + ".cast(o));"); System.out.print(indent + " "); System.out.print("}"); if (ch == (''A'' + i - 1)) { System.out.println(); } } System.out.print(indent); System.out.println("}"); } } // Generated code pasted below. public static void cswitch(Object o) { } public static <A> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } } public static <A, B> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } } public static <A, B, C> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } } public static <A, B, C, D> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC, Class<D> classD, Consumer<? super D> actionD) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } } public static <A, B, C, D, E> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC, Class<D> classD, Consumer<? super D> actionD, Class<E> classE, Consumer<? super E> actionE) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } } public static <A, B, C, D, E, F> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC, Class<D> classD, Consumer<? super D> actionD, Class<E> classE, Consumer<? super E> actionE, Class<F> classF, Consumer<? super F> actionF) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } } public static <A, B, C, D, E, F, G> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC, Class<D> classD, Consumer<? super D> actionD, Class<E> classE, Consumer<? super E> actionE, Class<F> classF, Consumer<? super F> actionF, Class<G> classG, Consumer<? super G> actionG) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } else if (classG.isInstance(o)) { actionG.accept(classG.cast(o)); } } public static <A, B, C, D, E, F, G, H> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC, Class<D> classD, Consumer<? super D> actionD, Class<E> classE, Consumer<? super E> actionE, Class<F> classF, Consumer<? super F> actionF, Class<G> classG, Consumer<? super G> actionG, Class<H> classH, Consumer<? super H> actionH) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } else if (classD.isInstance(o)) { actionD.accept(classD.cast(o)); } else if (classE.isInstance(o)) { actionE.accept(classE.cast(o)); } else if (classF.isInstance(o)) { actionF.accept(classF.cast(o)); } else if (classG.isInstance(o)) { actionG.accept(classG.cast(o)); } else if (classH.isInstance(o)) { actionH.accept(classH.cast(o)); } } }

Si desea generar sobrecargas, por ejemplo, para tener más de 8 casos, puede decir algo como lo siguiente:

GeneratedClassSwitch.generateFixedOverloads(16, 1);

Eso generará métodos para System.out que siguen la forma general de:

public static <A, B, C> void cswitch (Object o, Class<A> classA, Consumer<? super A> actionA, Class<B> classB, Consumer<? super B> actionB, Class<C> classC, Consumer<? super C> actionC) { if (classA.isInstance(o)) { actionA.accept(classA.cast(o)); } else if (classB.isInstance(o)) { actionB.accept(classB.cast(o)); } else if (classC.isInstance(o)) { actionC.accept(classC.cast(o)); } }

Tenga en cuenta que podemos asignar cada tipo de clase a su tipo de consumidor asociado, es decir, Class< A > con Consumer<? super A > Consumer<? super A > , Class< B > con Consumer<? super B > Consumer<? super B > , y así sucesivamente. Esto es realmente imposible con varargs (a partir de la versión actual de Java, de todos modos, que es 10).

Nuestro ejemplo de uso ahora es así, asumiendo nuevamente las importaciones, por ejemplo, de import static mcve.util.GeneratedClassSwitch.*; :

cswitch(anObject, Byte.class, b -> System.out.println("Byte"), Short.class, s -> System.out.println("Short"), Integer.class, i -> System.out.println("Integer"), Long.class, l -> System.out.println("Long"), Float.class, f -> System.out.println("Float"), Double.class, d -> System.out.println("Double") );

(Las notas sobre casos default y null son las mismas que en el primer ejemplo).

Antes de comenzar, sé que hay muchas respuestas a esta pregunta que sugieren enfoques alternativos. Estoy buscando ayuda para este enfoque particular en cuanto a si es posible, y si no, enfoques similares que podrían funcionar.

Tengo un método que toma una superclase y llama a un método basado en el tipo del objeto pasado. por ejemplo:

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

No puedo modificar los subtipos para anular un método handle() , como sugiere esta respuesta , porque no soy el propietario de las clases. Así que el método instanceof es todo lo que tengo.

Me gustaría usar una instrucción switch lugar de if/else , simplemente porque es mucho más ordenado. Sé que solo puedes activar primitivas y cadenas, así que estoy cambiando el nombre de la clase:

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

El problema aquí es que los nombres canónicos son MUY largos (como 12 subpaquetes), y no puedo llamar a ClassName.class.getCanonicalName() en la declaración del caso porque Java no lo permite. Así que mi siguiente solución fue un Enum. Aquí es donde encuentro mi problema.

Me gustaría que mi código se pareciera a esto:

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

Pero esto no se compila. No estoy seguro de por qué. Me gustaría un enfoque que me permita cambiar el tipo sin tener que escribir todo el nombre canónico. Si hago eso, también podría usar if/else y instanceof .

NOTA Hay un par de tipos que tienen el mismo nombre (clases internas), por lo que getSimpleName() está fuera.


El operador instanceof es un enfoque simple, cuando no eres el propietario de las clases. Una expresión instanceof es verdadera cuando el objeto es la clase dada o una subclase .

Mencionas que no eres el propietario de las clases. El propietario podría introducir subclases en una versión posterior. Digamos que el propietario presenta APlus como una subclase de A. Una instancia de APlus es un A. El código que funciona en una A también debería funcionar en un APlus. Si usa instanceof , su código continuará funcionando, sin el esfuerzo de usted. Si usa nombres de clase, fallaría, sin previo aviso de su compilador.

Si está cambiando repetidamente el mismo objeto, puede que le resulte útil envolver el objeto una vez en una clase de contenedor que implemente una interfaz. A partir de entonces, puede simplemente llamar a los métodos en la interfaz, sin if , switch o map.

public interface IWrapper { public void handle(); public String describe(); } public AWrapper implements IWrapper { ... } public BWrapper implements IWrapper { ... } public CWrapper implements IWrapper { ... } public UnknownWrapper implements IWrapper { ... } IWrapper wrap( Object o ) { if ( o instanceof A ) return new AWrapper((A) o); else if ( o instanceof B ) return new BWrapper((B) o); else if ( o instanceof C ) return new CWrapper((C) o); else return new UnknownWrapper(o); }

Incluso en la ausencia garantizada de subclases, evite especificar nombres de clases como cadenas literales en casos de switch . Esto permite errores que el compilador no encontrará, lo que puede costarle tiempo de depuración.


Estabas muy cerca de la solución con enumeraciones. No se ha compilado porque su enumeración del constructor y el método de cobertura faltan para mapear la enumeración desde String. En realidad, podría resolverlo incluso sin String, es decir, sin llamar a getCanonicalName en absoluto:

public enum Classes { // changed enum constants a bit to avoid confusing with target class names ClsA (A.class), ClsB (B.class), ClsC (C.class), UNKNOWN(null); private final Class<?> targetClass; Classes(Class<?> targetClass) { this.targetClass = targetClass; } public static Classes fromClass(Class<?> cls) { for(Classes c : values()) { if(c.targetClass == cls) return c; } return UNKNOWN; } } switch (Classes.fromClass(o.getClass())) { case ClsA: handleA((A)o); break; case ClsB: handleB((B)o); break; case ClsC: handleC((C)o); break; default: handleUnknown(o); break; }

Si obtiene un número significativo de clases conocidas, considere usar map en lugar de iterar en Classes.fromClass, por ejemplo:

public enum Classes { ClsA(A.class), ClsB(B.class), // etc... UNKNWON(null); // need a wrapper class to avoid compilation problem // with referring static enum field within an initializer private static class Holder { public static final IdentityHashMap<Class<?>, Classes> map = new IdentityHashMap<>(); } Classes(Class<?> targetClass) { Holder.map.put(targetClass, this); } public static Classes fromClass(Class<?> cls) { Classes c = Holder.map.get(cls); return c != null ? c : UNKNOWN; } }


Este es un enfoque que no trata con los nombres de clase en absoluto, y se distribuye tan rápido como lo hace una declaración de switch : haga un mapa hash para asignar objetos de Class<T> a manejadores específicos de clase, y use el mapa en lugar de un 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 }


Para cambiar los tipos de clase conocidos, puede utilizar el método de abajo.

Crear un Enum con nombres de clase.

public enum ClassNameEnum { ClassA, ClassB, ClassC }

Encuentra el nombre de clase del objeto. Escribir un caso de cambio sobre la enumeración.

private void switchByClassType(Object obj) { ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName()); switch (className) { case ClassA: doA(); break; case ClassB: doB(); break; case ClassC: doC(); break; } } }


Pude trabajar alrededor de java.lang.reflect

import java.lang.reflect.Method; public class MyClass { public void validate(Object o) { String className = o.getClass().getSimpleName(); try { //this line searches a method named as className Method m = this.getClass().getDeclaredMethod(className); //this line execute the method m.invoke(this); } catch (Exception e) { e.printStackTrace(); handleUnknown(); } } //this methot will execute if the object o is instance of A public void A() { } //this methot will execute if the object o is instance of B public void B() { } //this methot will execute if the object o is instance of C public void C() { } //this methot will execute if the method is unknown public void handleUnknown(){ } }


Usando java 8 lambdas puedes llegar a algo como esto:

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

Para hacer eso debes definir los siguientes métodos estáticos:

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