instanceof
객체에 스위치 케이스를 사용하는 것에 대한 질문이 있습니다 .
예를 들어, 내 문제는 Java로 재현 될 수 있습니다.
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
switch...case
?를 사용하여 어떻게 구현할 수 있습니까?
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;
}
클래스가 핵심이고 기능, 즉 람다 등이 값인 맵을 작성하십시오.
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();
예외를 발생시키고 Runnable 대신 예외를 사용하는 FunctionalInterface를 구현하는 것보다 확인 된 예외가 필요한 경우.
instanceof
중 하나입니다. 그 상자 ...)
누군가가 그것을 읽을 경우를 대비하여 :
자바에서 가장 좋은 해결책은 다음과 같습니다.
public enum Action {
a{
void doAction(...){
// some code
}
},
b{
void doAction(...){
// some code
}
},
c{
void doAction(...){
// some code
}
};
abstract void doAction (...);
}
이러한 패턴의 큰 장점은 다음과 같습니다.
당신은 그것을 좋아합니다 (아무것도 스위치 없음) :
void someFunction ( Action action ) {
action.doAction(...);
}
"d"라는 새로운 액션을 추가하는 경우 반드시 doAction (...) 메소드를 구현해야합니다.
참고 :이 패턴은 Joshua의 Bloch "Effective Java (2 판)"에 설명되어 있습니다.
@Override
의 각 구현 위의 요구 doAction()
?
action
하시겠습니까? 캐스케이드 외부 인스턴스 someFunction()
에 의해 올바른 action
? 이것은 단지 다른 수준의 간접 지향을 추가합니다.
당신은 할 수 없습니다. switch
문은 포함 할 수 있습니다 case
컴파일 시간 상수 및 정수 (자바 6까지 자바 7의 문자열)에 대한 평가 문을.
함수형 프로그래밍에서 찾고있는 것을 "패턴 일치"라고합니다.
최고의 답변에서 논의 된 바와 같이, 전통적인 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 세계에서 가장 자연스러운 패러다임이 아니므로주의해서 사용하십시오. 일반적인 메소드는 일치하는 값을 타입 캐스트하지 않아도되지만, 우리는 스칼라의 사례 클래스 와 같이 일치하는 객체를 분해하는 표준 방법이 없습니다 .
불행히도 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 문을 사용할 수 있습니다
MyEnum 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
}
이와 같은 스위치 문을 사용하는 것은 객체 지향 방식이 아닙니다. 대신 다형성 의 힘을 사용해야합니다 . 간단히 쓰기
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() }
}
상대적으로 많은 '케이스'
가있는 경우 성능이 중요한 상황에서 프로세스가 실행되는 경우 더 빠르게 작동하고 문장을 만듭니다.
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());
}
}
스위치는 byte, short, char, int, String 및 열거 유형에서만 작동 할 수 없습니다 (그리고 프리미티브의 객체 버전은 Java 버전에 따라 다르며 문자열은 switch
Java 7에서 사용할 수 있음 )
개인적으로 다음 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);
}
이제 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();
};
어느 쪽이든 스위치로 멋진 작업을 수행했습니다.
공통 인터페이스를 조작 할 수 있으면 열거 형을 추가하고 각 클래스가 고유 한 값을 반환하도록 할 수 있습니다. 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을 사용하는 경우 열거 형에 문자열 값을 입력하면 스위치 케이스 블록이 계속 작동합니다.
value
만 열거 상수를 구분하려면 필드는 중복 - 직접 상수를 사용할 수 있습니다 (당신처럼).
이건 어때요 ?
switch (this.name)
{
case "A":
doA();
break;
case "B":
doB();
break;
case "C":
doC();
break;
default:
console.log('Undefined instance');
}
this.getSimpleName()
해야합니다. 포스터와 JS가 혼동되는지 확실하지 않습니다 (예, 그는 콘솔을 사용하고 있습니다).
switch 문을 사용해야하는 이유가 있다고 생각합니다. xText 생성 코드를 사용하는 경우 가능합니다. 또는 다른 종류의 EMF 생성 클래스.
instance.getClass().getName();
클래스 구현 명의 캐릭터 라인을 돌려줍니다. 예 : org.eclipse.emf.ecore.util.EcoreUtil
instance.getClass().getSimpleName();
간단한 표현을 반환합니다. 예 : EcoreUtil
switch
으로 사용할 수 없습니다.case
"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"문에 대해 컴파일 타임 오류가 발생하기 때문에 큰 문제는 아닙니다.
TYPE
열거 형을 사용할 수 있으며 고유성이 보장됩니다 ( 이 답변 에서 수행 된 것처럼 ). 그러나 접근 방법 중 하나를 사용하면 이름을 바꿀 때 두 곳에서 리팩토링해야합니다.
TYPE = "A"
가 포함되지 않을 수 있습니다 . 특히 해당 클래스 외부에있는 경우 수동으로 수행 할 때 잊어 버릴 수도 있습니다. IntelliJ는 실제로 문자열이나 주석에서 클래스 이름의 발생을 찾지 만 구문 트리를 보는 대신 텍스트 검색 일 뿐이므로 오 탐지를 포함합니다.
다음은 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)
);
}
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";
}
}
클래스 이름으로 열거 형 을 만듭니다 .
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;
}
}
}
도움이 되었기를 바랍니다.
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 의 완전한 구현을 살펴볼 수 있습니다 .
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;
}
}
if
... else if
코드 보다 나은 점은 무엇입니까?
if
... else if
을 "goto"문 으로 바꾸는 것입니다 . 이는 Java와 같은 언어로 제어 흐름을 구현 하는 잘못된 방법입니다.