Можно ли использовать оператор instanceof в операторе switch?

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

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

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

Как это будет реализовано с помощью switch...case?

Ответ 1

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

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, вы можете применить шаблон посетителя для достижения того же.

Ответ 2

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

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;

}

Ответ 3

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

Решение BEST в java:

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

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

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

    };

    abstract void doAction (...);
}

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

  • Вы просто делаете это как (NO переключатели вообще):

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

ПРИМЕЧАНИЕ. Этот шаблон описан в Joshua Bloch "Эффективная Java (2nd Edition)"

Ответ 4

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

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.

Ответ 5

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

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

См. также Избегайте instanceof в Java

Ответ 6

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

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

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

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

Если вы можете манипулировать общим интерфейсом, вы можете добавить в перечисление и вернуть каждому классу уникальное значение. Вам не потребуется экземпляр 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, вы можете поместить строковые значения для перечисления, и блок case switch будет работать.

Ответ 11

Мне лично нравится следующий код 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");
            });

Будет выводить:

YYyy

В примере кода используются строки, но вы можете использовать любой тип объекта, включая класс. например .myCase(this.getClass(), (o) ->...

Требуется следующий фрагмент:

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

Ответ 12

Вы не можете переключаться только с байтом, коротким, char, int, String и перечисляемыми типами (и объектными версиями примитивов, это также зависит от вашей версии java, строки могут быть switch ed in in java 7)

Ответ 13

Как насчет этого?

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

Ответ 14

Я думаю, что есть причины использовать оператор switch. Если вы используете, возможно, код сгенерированный xText. Или другие типы генерируемых EMF классов.

instance.getClass().getName();

возвращает строку имени реализации класса. то есть:   org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

возвращает простое представление, например:   EcoreUtil

Ответ 15

Если вам нужно "переключиться" через класс типа объекта "this", этот ответ будет лучшим fooobar.com/questions/38643/...

Но если вам нужно применить "переключатель" к любой другой переменной. Я бы предложил другое решение. Определите следующий интерфейс:

public interface ClassTypeInterface {
    public String getType();
}

Внедрите этот интерфейс в каждом классе, который вы хотите "переключить". Пример:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

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

После этого вы можете использовать его следующим образом:

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

Единственное, что вам нужно заботиться - сохранить "типы" уникальными во всех классах, реализующих ClassTypeInterface. Это не большая проблема, потому что в случае любого пересечения вы получаете ошибку времени компиляции для оператора "switch-case".

Ответ 16

существует еще более простой способ эмуляции структуры коммутатора, которая использует instanceof, вы делаете это, создавая блок кода в своем методе и называя его меткой. Затем вы используете, если структуры для подражания операторам case. Если случай истинен, вы используете разрыв LABEL_NAME, чтобы выйти из структуры вашего временного переключателя.

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

Ответ 17

К сожалению, это невозможно из коробки, поскольку оператор switch-case ожидает константное выражение. Чтобы преодолеть это, можно использовать значения перечислений с именами классов, например:

public enum MyEnum {
   A(A.class.getName()), 
   B(B.class.getName()),
   C(C.class.getName());

private String refClassname;
private static final Map<String, MyEnum> ENUM_MAP;

MyEnum (String refClassname) {
    this.refClassname = refClassname;
}

static {
    Map<String, MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
        map.put(instance.refClassname, instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
}

public static MyEnum get(String name) {
    return ENUM_MAP.get(name);
 }
}

С этим можно использовать оператор switch следующим образом

MyEnumType type = MyEnum.get(clazz.getName());
switch (type) {
case A:
    ... // it A class
case B:
    ... // it B class
case C:
    ... // it C class
}

Ответ 18

Это будет работать быстрее и иметь смысл в случае
- нельзя менять классы моделей (внешняя библиотека)
- процесс выполняется в контексте, чувствительном к производительности
- у вас относительно много "дел"

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

Ответ 19

Создайте Enum с именами классов.

public enum ClassNameEnum {
    A, B, C
}

Найдите имя класса объекта. Напишите регистр переключателя над перечислением.

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

Надеюсь это поможет.

Ответ 20

Вот функциональный способ сделать это в Java 8, используя 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)
        );
    }

Ответ 21

Eclipse Modeling Framework имеет интересную идею, которая также учитывает наследование. Основная концепция определена в интерфейсе Switch: переключение выполняется с помощью метода doSwitch.

Что действительно интересно, так это реализация. Для каждого типа интереса,

public T caseXXXX(XXXX object);

метод должен быть реализован (с реализацией по умолчанию, возвращающей нуль). Реализация doSwitch попытается вызвать все методы caseXXX для объекта для всей его иерархии типов. Что-то в строках:

BaseType baseType = (BaseType)object;
T result = caseBaseType(eAttribute);
if (result == null) result = caseSuperType1(baseType);
if (result == null) result = caseSuperType2(baseType);
if (result == null) result = caseSuperType3(baseType);
if (result == null) result = caseSuperType4(baseType);
if (result == null) result = defaultCase(object);
return result;

Фактическая структура использует целочисленный идентификатор для каждого класса, таким образом, логика - фактически чистый переключатель:

public T doSwitch(Object object) {
    return doSwitch(object.class(), eObject);
}

protected T doSwitch(Class clazz, Object object) {
    return doSwitch(getClassifierID(clazz), object);
}

protected T doSwitch(int classifierID, Object theObject) {
    switch (classifierID) {
    case MyClasses.BASETYPE:
    {
      BaseType baseType = (BaseType)object;
      ...
      return result;
    }
    case MyClasses.TYPE1:
    {
      ...
    }
  ...

Вы можете взглянуть на полную реализацию ECoreSwitch, чтобы получить лучшую идею.

Ответ 22

Несмотря на то, что невозможно написать оператор switch, возможно перейти к определенной обработке для каждого данного типа. Один из способов сделать это - использовать стандартный механизм двойной отправки. Примером, в котором мы хотим "переключиться" на основе типа, является сопоставитель исключений Jersey, где нам необходимо сопоставить множество исключений с ответами об ошибках. В то время как для этого конкретного случая, вероятно, есть лучший способ (то есть использование полиморфного метода, который переводит каждое исключение в ответ об ошибке), использование механизма двойной отправки все еще полезно и практично.

interface Processable {
    <R> R process(final Processor<R> processor);
}

interface Processor<R> {
    R process(final A a);
    R process(final B b);
    R process(final C c);
    // for each type of Processable
    ...
}

class A implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class B implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class C implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

Затем, где бы ни понадобился "переключатель", вы можете сделать это следующим образом:

public class LogProcessor implements Processor<String> {
    private static final Logger log = Logger.for(LogProcessor.class);

    public void logIt(final Processable base) {
        log.info("Logging for type {}", process(base));
    }

    // Processor methods, these are basically the effective "case" statements
    String process(final A a) {
        return "Stringifying A";
    }

    String process(final B b) {
        return "Stringifying B";
    }

    String process(final C c) {
        return "Stringifying C";
    }
}