¿Switch instanceof?

Tengo una pregunta sobre el uso de la caja del interruptor para instanceof objeto:

Por ejemplo: mi problema puede ser reproducido en Java:

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

¿Cómo se implementaría usando el switch...case ?

Este es un escenario típico donde el subtipo de polymorphism ayuda. Haz lo siguiente

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

Entonces simplemente puedes llamar a do() en this .

Si no puede cambiar A , B y C , puede aplicar el patrón de visitante para lograr lo mismo.

Si no puede codificar a una interfaz, puede usar una enumeración como intermediario:

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

En caso de que alguien lo lea:

La mejor solución en java es:

 public enum Action { a{ void doAction(...){ // some code } }, b{ void doAction(...){ // some code } }, c{ void doAction(...){ // some code } }; abstract void doAction (...); } 

Los GRANDES beneficios de dicho patrón son:

  1. Simplemente hazlo como (SIN interruptores):

     void someFunction ( Action action ) { action.doAction(...); } 
  2. En caso de que agregue una nueva Acción llamada “d”, DEBE imponer el método doAction (…)

NOTA: Este patrón se describe en Joshua’s Bloch “Effective Java (2nd Edition)”

No puedes. La sentencia switch solo puede contener sentencias case que son constantes de tiempo de comstackción y que se evalúan en un entero (Up to Java 6 y una cadena en Java 7).

Lo que está buscando se llama “coincidencia de patrones” en la progtwigción funcional.

Véase también Evitar instanceof en Java

Simplemente cree un Mapa donde la clase es la clave y la funcionalidad, es decir, lambda o similar, es el valor.

 Map doByClass = new HashMap<>(); doByClass.put(Foo.class, () -> doAClosure(this)); doByClass.put(Bar.class, this::doBMethod); doByClass.put(Baz.class, new MyCRunnable()); 

// por supuesto, refactorizar esto para solo inicializar una vez

 doByClass.get(getClass()).run(); 

Si necesita Excepciones marcadas, implemente una Interfaz funcional que arroje la Excepción y la use en lugar de Ejecutable.

Como se discutió en las respuestas principales, el enfoque tradicional de OOP es usar polymorphism en lugar de cambiar. Incluso hay un patrón de refactorización bien documentado para este truco: Reemplazar condicional con polymorphism . Cada vez que bash este enfoque, me gusta implementar también un objeto nulo para proporcionar el comportamiento predeterminado.

Comenzando con Java 8, podemos usar lambdas y generics para darnos algo que los progtwigdores funcionales están muy familiarizados con: la coincidencia de patrones. No es una función de lenguaje central, pero la biblioteca de Javaslang proporciona una implementación. Ejemplo del javadoc :

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

No es el paradigma más natural en el mundo de Java, así que úsalo con precaución. Si bien los métodos generics le evitarán tener que encasillar el valor coincidente, nos falta una forma estándar de descomponer el objeto coincidente como, por ejemplo, con las clases de casos de Scala .

Sé que esto es muy tarde, pero para los lectores futuros …

Tenga cuidado con los enfoques anteriores que se basan únicamente en el nombre de la clase de A , B , C …:

A menos que pueda garantizar que A , B , C … (todas las subclases o implementadores de Base ) son finales, entonces las subclases de A , B , C … no serán tratadas.

Aunque el enfoque if, elseif, elseif es más lento para un gran número de subclases / implementadores, es más preciso.

No, no hay forma de hacer esto. Sin embargo, lo que podría querer hacer es considerar el polymorphism como una forma de manejar este tipo de problemas.

Usar instrucciones de conmutación como esta no es la forma orientada a objetos. En su lugar, debe usar el poder del polymorphism . Simplemente escribe

 this.do() 

Habiendo configurado previamente una clase base:

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

que es la clase base para A , B y 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() } } 

Si puede manipular la interfaz común, puede agregar una enumeración y hacer que cada clase devuelva un valor único. No necesitará instanceof o un patrón de visitante.

Para mí, la lógica tenía que estar escrita en la sentencia switch, no en el objeto en sí. Esta fue mi solución:

ClassA, ClassB, and ClassC implement CommonClass

Interfaz:

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

Si está en java 7, puede poner valores de cadena para la enumeración y el bloque de caja de conmutación seguirá funcionando.

No se puede cambiar solo funciona con byte, short, char, int, String y enumerated types (y las versiones de objeto de las primitivas, también depende de tu versión java, las cadenas se pueden switch en java 7)

Qué tal esto ?

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

Personalmente me gusta el siguiente código 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"); }); 

Se producirá:

 YYyy 

El código de ejemplo usa cadenas pero puede usar cualquier tipo de objeto, incluida la clase. ej. .myCase(this.getClass(), (o) -> ...

Necesita el siguiente fragmento:

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

Creo que hay razones para usar una statement de cambio. Si está utilizando el código generado xText quizás. O otro tipo de clases generadas por EMF.

 instance.getClass().getName(); 

devuelve un String of the Class Implementation Name. es decir: org.eclipse.emf.ecore.util.EcoreUtil

 instance.getClass().getSimpleName(); 

devuelve la representación simple, es decir: EcoreUtil

Si necesita “cambiar” a través del tipo de clase de “este” objeto, esta respuesta es la mejor https://stackoverflow.com/a/5579385/2078368

Pero si necesita aplicar “cambiar” a cualquier otra variable. Sugeriría otra solución. Definir la siguiente interfaz:

 public interface ClassTypeInterface { public String getType(); } 

Implementa esta interfaz en cada clase que quieras “cambiar”. Ejemplo:

 public class A extends Something implements ClassTypeInterface { public final static String TYPE = "A"; @Override public String getType() { return TYPE; } } 

Después de eso puedes usarlo de la siguiente manera:

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

Lo único que debería interesarle es mantener los “tipos” únicos en todas las clases que implementan ClassTypeInterface. No es un gran problema, porque en caso de cualquier intersección, recibe un error de tiempo de comstackción para la sentencia “switch-case”.

hay una forma aún más simple de emular una estructura de conmutación que usa instanceof, esto se hace creando un bloque de código en su método y nombrándolo con una etiqueta. Luego, usa si las estructuras para emular las declaraciones de casos. Si un caso es verdadero, entonces usa el salto LABEL_NAME para salir de su estructura de cambio improvisada.

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

Esto funcionará más rápido y hará sentido en caso de que
– no puede cambiar las clases modelo (biblioteca externa)
– el proceso se ejecuta en el contexto sencitivo de rendimiento
– tienes relativamente muchos ‘casos’

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

Crea un Enum con nombres de clase.

 public enum ClassNameEnum { A, B, C } 

Encuentra el nombre de clase del objeto. Escribe una caja de cambio sobre la enumeración.

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

Espero que esto ayude.

Aquí hay una forma funcional de lograrlo en Java 8 usando 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) ); } 
    Intereting Posts