Switch instanceof?

j'ai une question d'utiliser le boîtier d'interrupteur pour instanceof objet:

par exemple: mon problème peut être reproduit en Java:

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

comment serait-il implémenté en utilisant switch...case ?

201
demandé sur yAnTar 2011-04-07 14:03:28

19 réponses

c'est un scénario typique où le polymorphisme des sous-types aide. Procédez de la manière suivante

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

, Alors vous pouvez simplement appeler do() sur this .

si vous n'êtes pas libre de changer A , B , et C , vous pouvez appliquer le modèle de visiteur pour obtenir le même.

187
répondu jmg 2011-04-07 10:13:16

si vous ne pouvez absolument pas coder à une interface, alors vous pouvez utiliser un enum comme un intermédiaire:

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;

}
73
répondu Nico 2011-04-07 11:45:48

juste au cas où quelqu'un le lirait:

la meilleure solution en java est:

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

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

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

    };

    abstract void doAction (...);
}

les grands avantages d'un tel modèle sont:

  1. Vous venez de le faire (PAS de commutateurs à tous):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
    
  2. Dans le cas où si vous ajouter une nouvelle Action appelé "d" vous DEVEZ imlement doAction(...) méthode

NOTE: Ce modèle est décrit dans le Bloch de Joshua "Java efficace (2e édition) "

33
répondu se.solovyev 2011-10-10 10:07:24

vous ne pouvez pas. La déclaration switch ne peut contenir que des déclarations case qui sont des constantes de temps de compilation et qui évaluent à un entier (Jusqu'à Java 6 et une chaîne en Java 7).

ce que vous recherchez s'appelle" correspondance des motifs " dans la programmation fonctionnelle.

Voir aussi , en Évitant instanceof en Java

15
répondu Carlo V. Dango 2017-05-23 12:10:41

il suffit de créer une Carte où la classe est la clé et la fonctionnalité, c'est à dire lambda ou similaire, est la valeur.

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

// bien sûr, refactoriser qu'à initialiser une fois

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

si vous avez besoin d'Exceptions vérifiées que d'implémenter une Fonctionnaleinterface qui lance L'Exception et l'utiliser à la place de Runnable.

15
répondu Novaterata 2017-02-28 15:50:14

comme on l'a vu dans les réponses du haut, l'approche traditionnelle de L'OOP consiste à utiliser le polymorphisme plutôt que le switch. Il existe même un modèle de recadrage bien documenté pour cette astuce: remplacer conditionnel par polymorphisme . Chaque fois que j'adopte cette approche, j'aime aussi implémenter un Null object pour fournir le comportement par défaut.

à partir de Java 8, nous pouvons utiliser lambdas et génériques pour nous donner quelque chose de fonctionnel les programmeurs sont très familiers avec: l'appariement des motifs. Ce n'est pas une fonctionnalité de langage de base, mais la bibliothèque Javaslang fournit une implémentation. Exemple tiré du javadoc :

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

ce n'est pas le paradigme le plus naturel dans le monde Java alors utilisez-le avec prudence. Alors que les méthodes génériques vous éviteront d'avoir à taper la valeur appariée, il nous manque une méthode standard pour décomposer l'objet apparié comme avec Scala en cas de classes par exemple.

12
répondu Pavel 2015-09-09 06:37:34

je sais que c'est très tard, mais pour les futurs lecteurs ...

méfiez-vous des approches ci-dessus qui sont basées uniquement sur le nom de la classe de A , B , c ... :

Sauf si vous pouvez garantir que Un , B , C ... (toutes les sous-classes ou les exécutants de Base ) sont finale , puis sous-classes de Un , B , C ... elle ne sera pas traitée.

même si le si, elseif, elseif .. L'approche est plus lente pour un grand nombre de sous-classes/exécutants, elle est plus précise.

7
répondu JohnK 2014-02-27 10:50:23

Non, il n'y a aucun moyen de le faire. Ce que vous pourriez vouloir faire est cependant de considérer polymorphisme comme un moyen de gérer ce genre de problèmes.

5
répondu Andreas Johansson 2011-04-07 10:09:28

L'utilisation d'instructions de commutation comme celle-ci n'est pas orientée objet. Vous devriez plutôt utiliser le pouvoir de polymorphisme . Il suffit d'écrire

this.do()

ayant préalablement mis en place une classe de base:

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

qui est la classe de base pour A , B et 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() }
}
5
répondu Raedwald 2011-04-07 10:10:54

si vous pouvez manipuler l'interface commune, vous pouvez ajouter un enum et demander à chaque classe de retourner une valeur unique. Vous n'aurez pas besoin d'instanceof ou d'un motif de visiteur.

pour moi, la logique devait être dans l'écrit dans la déclaration switch, pas l'objet lui-même. C'était ma solution:

ClassA, ClassB, and ClassC implement CommonClass

de l'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;
  }
...

si vous êtes sur java 7, vous pouvez mettre des valeurs de chaîne pour l'enum et le bloc switch case fonctionnera toujours.

3
répondu Gaʀʀʏ 2017-03-24 14:58:05

Vous ne pouvez pas d'un interrupteur ne fonctionne qu'avec le byte, short, char, int, String et les types énumérés (et l'objet de versions primitives, cela dépend aussi de votre version de java, les Chaînes peuvent être switch ed dans java 7)

2
répondu Tnem 2011-04-07 10:24:54

et ça ?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}
2
répondu Joeri 2013-02-08 14:14:19

j'aime personnellement le code Java 1.8 suivant:

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

sortira:

YYyy

le code de l'échantillon utilise des chaînes mais vous pouvez utiliser n'importe quel type d'objet, y compris la classe. par exemple .myCase(this.getClass(), (o) -> ...

nécessite l'extrait suivant:

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);
}
2
répondu Feiteira 2018-01-09 11:24:27

je pense qu'il y a des raisons d'utiliser un énoncé d'interrupteur. Si vous utilisez le Code généré par xText peut-être. Ou un autre type de classes générées par les CEM.

instance.getClass().getName();

renvoie une chaîne de caractères du nom D'implémentation de la classe. j'.e: org.Eclipse.EMF.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

renvoie la représentation simple I. e: EcoreUtil

1
répondu Thomas Haarhoff 2013-04-02 08:03:11

si vous avez besoin de "switch" à travers le type de classe de "cet" objet, cette réponse est la meilleure https://stackoverflow.com/a/5579385/2078368

mais si vous devez appliquer" switch " à toute autre variable. Je suggérerais une autre solution. Définissez l'interface suivante:

public interface ClassTypeInterface {
    public String getType();
}

implémentez cette interface dans chaque classe que vous voulez"basculer". Exemple:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

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

après cela, vous pouvez l'utiliser de la manière suivante:

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

la seule chose dont vous devez vous soucier - gardez les" types " uniques à travers toutes les classes implémentant L'interface de Classstypeinterface. Ce n'est pas un gros problème, car en cas d'intersection vous recevez une erreur de compilation pour l'instruction "switch-case".

1
répondu Sergey Krivenkov 2017-05-23 12:26:36

il y a une façon encore plus simple d'émuler une structure de commutateur qui utilise instanceof, vous le faites en créant un bloc de code dans votre méthode et en la nommant avec une étiquette. Ensuite, vous utilisez les structures if pour émuler les énoncés de cas. Si un cas est vrai, alors vous utilisez le label_name de rupture pour sortir de votre structure de commutateur de fortune.

        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;
            }
        }
1
répondu Maurice 2017-06-26 12:38:39

cela fonctionnera plus rapidement et fera sence dans le cas

- vous ne pouvez pas changer les classes de modèle (bibliothèque externe)

- process is executed in performance sensitive context

- vous avez relativement beaucoup de "cas

public static <T> 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());
    }
}
0
répondu Mykhaylo Adamovych 2018-06-13 11:47:45

créer un Enum avec les noms de classe.

public enum ClassNameEnum {
    A, B, C
}

trouver le nom de classe de l'objet. Écrivez un switch case au-dessus de l'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;
        }
    }
}

Espérons que cette aide.

0
répondu Siva Kumar 2018-06-27 05:21:30

Voici une façon fonctionnelle de l'accomplir en Java 8 en utilisant 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)
        );
    }
0
répondu Viswanath 2018-07-19 08:30:19