Переключатель оператор instanceof?



у меня есть вопрос об использовании switch case для

541   19  

19 ответов:

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

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() on this.

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

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

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;

}

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

лучшее решение в 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-е издание)"

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

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

см. также избегая instanceof в Java

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

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.

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

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

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

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

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

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

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

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

нет, нет никакого способа сделать это. То, что вы можете сделать, это, однако, рассмотреть полиморфизм как способ справиться с такого рода проблемами.

использование операторов 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() }
}

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

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

ClassA, ClassB, and ClassC implement CommonClass

интерфейс:

public interface CommonClass {
   MyEnum getEnumType();
}

перечисление:

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

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

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

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

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

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

instance.getClass().getName();

возвращает строку имени класса реализации. я.е: орг.затмение.ЭДС.Ecore-моделью.утиль.EcoreUtil

instance.getClass().getSimpleName();

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

Если вам нужно "переключиться" через тип класса" этого " объекта, этот ответ является лучшим https://stackoverflow.com/a/5579385/2078368

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

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

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

существует еще более простой способ эмуляции структуры коммутатора, который использует instanceof, вы делаете это, создавая блок кода в своем методе и называя его меткой. Затем вы используете структуры if для эмуляции операторов case. Если случай истинен, то вы используете break 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;
            }
        }

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

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

создать перечисление С именами классов.

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

надеюсь, что это помогает.

вот функциональный способ его выполнения в 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)
        );
    }

Comments

    Ничего не найдено.