instanceof
オブジェクトに switch case を使用することについて質問があります。
例: 私の問題は Java で再現できます。
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
を使用してどのように実装されswitch...case
ますか?
instanceof
オブジェクトに switch case を使用することについて質問があります。
例: 私の問題は 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;
}
誰かがそれを読む場合に備えて:
Javaでの最良の解決策は次のとおりです。
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 (2nd Edition)" で説明されています。
できません。ステートメントには、コンパイル時の定数であり、整数に評価されるステートメントswitch
のみを含めることができますcase
(Java 6 まで、Java 7 では文字列)。
あなたが探しているものは、関数型プログラミングで「パターンマッチング」と呼ばれています。
Java での instanceof の回避も参照してください。
上位の回答で説明したように、従来の OOP アプローチは、スイッチの代わりにポリモーフィズムを使用することです。このトリックには、十分に文書化されたリファクタリング パターンもあります: Replace Conditional with Polymorphism。このアプローチに到達するときはいつでも、デフォルトの動作を提供するためにNull オブジェクトも実装するのが好きです。
Java 8 以降では、ラムダとジェネリックを使用して、関数型プログラマーがよく知っているパターン マッチングを実現できます。これはコア言語機能ではありませんが、VAVR ライブラリ(以前の Javaslang ライブラリは 1 つの実装を提供します) です。ドキュメントの例:
Match.ofType(Number.class)
.caze((Integer i) -> i)
.caze((String s) -> new BigDecimal(s))
.orElse(() -> -1)
.apply(1.0d); // result: -1
これは Java の世界で最も自然なパラダイムではないため、注意して使用してください。ジェネリック メソッドを使用すると、一致した値を型キャストする必要がなくなりますが、たとえばScala のケース クラスのように、一致したオブジェクトを分解する標準的な方法がありません。
残念ながら、switch-case ステートメントは定数式を想定しているため、すぐに使用することはできません。これを克服するための 1 つの方法は、クラス名で列挙型の値を使用することです。
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
}
私はこれが非常に遅いことを知っていますが、将来の読者のために...
A、B、C ...のクラスの名前のみに基づく上記のアプローチに注意してください。
A、B、C ... ( Baseのすべてのサブクラスまたは実装者) がfinalであることを保証できない限り、A、B、C ... のサブクラスは処理されません。
if、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() }
}
スイッチは、バイト、ショート、char、int、文字列、および列挙型でのみ機能することはできません(およびプリミティブのオブジェクトバージョン。Javaバージョンにも依存します。文字列switch
はJava 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;
}
実装:
...
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');
}
クラス名でEnumを作成します。
public enum ClassNameEnum {
A, B, C
}
オブジェクトのクラス名を見つけます。列挙型にswitchケースを記述します。
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)
);
}