Можно ли использовать оператор instanceof в операторе switch? - PullRequest
233 голосов
/ 07 апреля 2011

У меня вопрос об использовании регистра переключателя для instanceof объекта:

Например: моя проблема может быть воспроизведена на Java:

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

Как бы это реализовать, используя switch...case?

Ответы [ 22 ]

206 голосов
/ 07 апреля 2011

Это типичный сценарий, в котором помогает полиморфизм подтипа.Выполните следующие действия:

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

Затем вы можете просто позвонить do() на this.

Если вы не можете изменить A, B и C, вы можете применить шаблон посетителя для достижения того же.

83 голосов
/ 07 апреля 2011

если вы абсолютно не можете кодировать интерфейс, вы можете использовать enum в качестве посредника:

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;

}
34 голосов
/ 10 октября 2011

На всякий случай, если кто-то прочтет это:

ЛУЧШЕЕ решение в java:

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

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

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

    };

    abstract void doAction (...);
}

БОЛЬШИЕ преимущества такого шаблона:

  1. Вы просто делаете это следующим образом (НЕТ переключателей вообще):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
    
  2. В случае, если вы добавляете новое действие под названием "d", вы ДОЛЖНЫ имплементировать doAction (...) метод

ПРИМЕЧАНИЕ. Этот шаблон описан в блоке Джошуа «Эффективная Java (2-е издание)»

27 голосов
/ 28 февраля 2017

Просто создайте карту, где класс является ключом, а функциональность, то есть лямбда или аналогичная, является значением.

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

// конечно, рефакторинг, чтобы инициализировать только один раз

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

Если вам нужны проверенные исключения, то реализуйте интерфейс FunctionalInterface, который выдает исключение и использует его вместо Runnable.

17 голосов
/ 07 апреля 2011

Вы не можете.Оператор switch может содержать только операторы case, которые являются постоянными времени компиляции и которые оцениваются как целое число (до Java 6 и строка в Java 7).

То, что вы ищете, называется «сопоставлением с образцом» в функциональном программировании.

См. Также Как избежать instanceof в Java

16 голосов
/ 09 сентября 2015

Как обсуждалось в верхних ответах, традиционный подход ООП заключается в использовании полиморфизма вместо переключателя.Для этого трюка есть даже хорошо документированный шаблон рефакторинга: Заменить условное на полиморфизм .Всякий раз, когда я достигаю этого подхода, мне также нравится реализовывать Null-объект для обеспечения поведения по умолчанию.

Начиная с Java 8, мы можем использовать лямбда-выражения и обобщенные выражения, чтобы дать нам что-то функциональное программированиеочень знакомы с: сопоставление с образцом.Это не базовая языковая функция, но библиотека Javaslang обеспечивает одну реализацию.Пример из javadoc :

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

Это не самая естественная парадигма в мире Java, поэтому используйте ее с осторожностью.Хотя универсальные методы избавят вас от необходимости типизировать совпадающее значение, мы упускаем стандартный способ декомпозиции сопоставляемого объекта, как, например, классы case Scala .

7 голосов
/ 27 февраля 2014

Я знаю, что уже очень поздно, но для будущих читателей ...

Остерегайтесь описанных выше подходов, основанных только на имени класса A , B , C .. .:

Если вы не можете гарантировать, что A , B , C ... (все подклассы или реализации Base ) final , тогда подклассы A , B , C ... не будут рассматриваться.

Несмотря на то, что подход if, elseif, elseif .. медленнее для большого числа подклассов / реализаторов, он более точен.

5 голосов
/ 07 апреля 2011

Использование таких операторов переключения не является объектно-ориентированным способом.Вместо этого вы должны использовать силу полиморфизм .Просто напишите

this.do()

Предварительно настроив базовый класс:

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

, который является базовым классом для A, B и 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 голосов
/ 07 апреля 2011

Нет, нет способа сделать это.Однако, возможно, вы захотите рассмотреть Полиморфизм как способ решения подобных проблем.

3 голосов
/ 08 октября 2013

Если вы можете манипулировать общим интерфейсом, вы можете добавить add в enum и заставить каждый класс возвращать уникальное значение.Вам не понадобится instanceof или шаблон посетителя.

Для меня логика должна быть записана в операторе switch, а не в самом объекте.Это было мое решение:

ClassA, ClassB, and ClassC implement CommonClass

Интерфейс:

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

Если вы используете Java 7, вы можете поместить строковые значения для enum, и блок регистра переключателей будет по-прежнему работать.

...