Programing

switch 문에서 instanceof 연산자를 사용할 수 있습니까?

lottogame 2020. 4. 8. 07:35
반응형

switch 문에서 instanceof 연산자를 사용할 수 있습니까?


instanceof객체에 스위치 케이스를 사용하는 것에 대한 질문이 있습니다 .

예를 들어, 내 문제는 Java로 재현 될 수 있습니다.

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

switch...case?를 사용하여 어떻게 구현할 수 있습니까?


이는 하위 유형 다형성이 도움이되는 일반적인 시나리오입니다. 다음을 수행하십시오

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, 당신은 동일을 달성하기 위해 방문자 패턴을 적용 할 수 있습니다.


인터페이스에 코드를 작성할 수 없다면 열거 형을 중개자로 사용할 수 있습니다.

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;

}

누군가가 그것을 읽을 경우를 대비하여 :

자바에서 가장 좋은 해결책은 다음과 같습니다.

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 (...) 메소드를 구현해야합니다.

참고 :이 패턴은 Joshua의 Bloch "Effective Java (2 판)"에 설명되어 있습니다.


클래스가 핵심이고 기능, 즉 람다 등이 값인 맵을 작성하십시오.

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

예외를 throw하고 Runnable 대신 예외를 사용하는 FunctionalInterface를 구현하는 것보다 확인 된 예외가 필요한 경우.


당신은 할 수 없습니다. switch문은 포함 할 수 있습니다 case컴파일 시간 상수 및 정수 (자바 6까지 자바 7의 문자열)에 대한 평가 문을.

함수형 프로그래밍에서 찾고있는 것을 "패턴 일치"라고합니다.

참조 자바 instanceof를 방지


최고의 답변에서 논의 된 바와 같이, 전통적인 OOP 방식은 스위치 대신 다형성을 사용하는 것입니다. 이 트릭에는 잘 문서화 된 리팩토링 패턴이 있습니다 : 조건부를 다형성으로 바꾸기 . 이 접근법에 도달 할 때마다 기본 동작을 제공하기 위해 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 세계에서 가장 자연스러운 패러다임이 아니므로주의해서 사용하십시오. 일반적인 방법을 사용하면 일치하는 값을 타입 캐스트하지 않아도되지만 스칼라의 사례 클래스 와 같이 일치하는 객체를 분해하는 표준 방법이 없습니다 .


나는 이것이 매우 늦었지만 장래 독자들을 위해 ...

A , B , C ... 클래스의 이름 에만 기반한 위의 접근 방식에주의하십시오 .

당신은 보장 할 수없는 한 A는 , B , C가 ... (모든 서브 클래스 또는 구현 자료가 ) 있습니다 마지막 의 다음 서브 클래스 , B , C가 ... 처리되지 않습니다.

많은 수의 서브 클래스 / 구현자가 if, elseif, elseif .. 접근 방식이 느리 더라도 더 정확합니다.


아니요,이 방법은 없습니다. 그러나 당신이하고 싶은 것은 다형성 을 이런 종류의 문제를 처리하는 방법으로 간주 하는 것입니다.


이와 같은 스위치 문을 사용하는 것은 객체 지향 방식이 아닙니다. 대신 다형성 의 힘을 사용해야합니다 . 간단히 쓰기

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

개인적으로 다음 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);
}

스위치는 byte, short, char, int, String 및 열거 유형에서만 작동 할 수 없습니다 (그리고 프리미티브의 객체 버전은 Java 버전에 따라 다르며 문자열은 switchJava 7에서 사용할 수 있음 )


공통 인터페이스를 조작 할 수 있으면 열거 형을 추가하고 각 클래스가 고유 한 값을 반환하도록 할 수 있습니다. 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을 사용하는 경우 열거 형에 문자열 값을 넣을 수 있으며 스위치 케이스 블록은 계속 작동합니다.


이것은 어떤가요 ?

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

불행히도 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's A class
case B:
    ... // it's B class
case C:
    ... // it's C class
}


상대적으로 많은 '케이스'
가있는 경우 성능이 중요한 상황에서 프로세스가 실행되는 경우 더 빠르게 작동하고 문장을 만듭니다.

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

switch 문을 사용해야하는 이유가 있다고 생각합니다. xText 생성 코드를 사용하는 경우 가능합니다. 또는 다른 종류의 EMF 생성 클래스.

instance.getClass().getName();

클래스 구현 명의 캐릭터 라인을 돌려줍니다. 예 : org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

간단한 표현을 반환합니다. 예 : EcoreUtil


"this"객체의 클래스 유형을 통해 "전환"해야하는 경우이 답변이 가장 좋습니다. 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;
    }
    ...
}

주의해야 할 것은-ClassTypeInterface를 구현하는 모든 클래스에서 "유형"을 고유하게 유지하는 것입니다. 교차점의 경우 "switch-case"문에 대해 컴파일 타임 오류가 발생하기 때문에 큰 문제는 아닙니다.


instanceof를 사용하는 스위치 구조를 에뮬레이트하는 더 간단한 방법이 있습니다. 메소드에서 코드 블록을 작성하고 레이블을 사용하여 이름을 지정하면됩니다. 그런 다음 if 구조를 사용하여 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;
            }
        }

클래스 이름으로 열거 형만듭니다 .

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

도움이 되었기를 바랍니다.


다음은 http://www.vavr.io/를 사용하여 Java 8에서 기능을 수행하는 방법입니다 .

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

Eclipse Modeling Framework에는 상속을 고려한 흥미로운 아이디어가 있습니다. 기본 개념은 스위치 인터페이스에 정의되어 있습니다 . 전환은 doSwitch 메소드 를 호출하여 수행됩니다 .

실제로 흥미로운 것은 구현입니다. 관심있는 각 유형에 대해

public T caseXXXX(XXXX object);

메소드를 구현해야합니다 (기본 구현은 null을 리턴 함). 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;

실제 프레임 워크는 각 클래스에 정수 ID를 사용하므로 논리는 실제로 순수한 스위치입니다.

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 의 완전한 구현을 살펴볼 수 있습니다 .


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

이제 Java를 통해 OP 방식으로 전환 할 수 있습니다. 스위치 라고하는 패턴 일치 라고합니다 . 현재 초안이지만 현재 스위치에 얼마나 많은 작업을했는지보고 있습니다. JEP에 주어진 예는

String formatted;
switch (obj) {
    case Integer i: formatted = String.format("int %d", i); break;
    case Byte b:    formatted = String.format("byte %d", b); break;
    case Long l:    formatted = String.format("long %d", l); break;
    case Double d:  formatted = String.format("double %f", d); break;
    case String s:  formatted = String.format("String %s", s); break
    default:        formatted = obj.toString();
}  

또는 그들의 람다 구문을 사용하고 값을 반환

String formatted = 
    switch (obj) {
        case Integer i -> String.format("int %d", i)
        case Byte b    -> String.format("byte %d", b);
        case Long l    -> String.format("long %d", l); 
        case Double d  -> String.format("double %f", d); 
        case String s  -> String.format("String %s", s); 
        default        -> obj.toString();
    };

어느 쪽이든 그들은 스위치로 멋진 일을하고있었습니다.

참고 URL : https://stackoverflow.com/questions/5579309/is-it-possible-to-use-the-instanceof-operator-in-a-switch-statement

반응형