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());
//もちろん、これをリファクタリングして1回だけ初期化する
doByClass.get(getClass()).run();
例外をチェックする必要がある場合は、例外をスローするFunctionalInterfaceを実装し、Runnableの代わりにそれを使用します。
instanceof
。そのボックス...)
誰かがそれを読む場合に備えて:
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(...)メソッドを実装する必要があります。
注:このパターンは、ジョシュアのブロック「Effective Java(2nd Edition)」で説明されています
@Override
各実装の上で必要doAction()
ですか?
action
を使用するかをどのように決定しますか?someFunction()
正しいで呼び出す外側のinstanceof-cascadeによってaction
?これにより、別のレベルの間接参照が追加されます。
できません。switch
声明は含めることができcase
、コンパイル時定数であり、整数(Javaの6までとJava 7の文字列)を評価する発言を。
関数型プログラミングでは、探しているものを「パターンマッチング」と呼びます。
Javaでのinstanceofの回避も参照してください。
上位の回答で説明したように、従来のOOPアプローチは、スイッチの代わりにポリモーフィズムを使用することです。このトリックには、十分に文書化されたリファクタリングパターンもあります。条件付きをポリモーフィズムに置き換えます。このアプローチに到達するときはいつでも、デフォルトの動作を提供するためにNullオブジェクトも実装するのが好きです。
Java 8以降では、ラムダとジェネリックを使用して、関数型プログラマーが非常に精通しているもの、つまりパターンマッチングを使用できるようになりました。これはコア言語機能ではありませんが、Javaslangライブラリは1つの実装を提供します。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クラスのように、一致したオブジェクトを分解する標準的な方法がありません。
残念ながら、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
}
いいえ、これを行う方法はありません。ただし、ポリモーフィズムをこのような問題に対処する方法として検討することをお勧めします。
このような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() }
}
これはより速く動作し、ケース
-比較的多くの「ケース」がある
-でパフォーマンスを重視するプロセスが実行されます
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バージョンにも依存し、Stringは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;
}
インプリ:
...
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を使用している場合は、enumに文字列値を置くことができ、switch caseブロックは引き続き機能します。
value
あなたが唯一のenum定数を区別したい場合、このフィールドは冗長である-あなたは(あなたがそうであるように)直接定数を使用することができます。
これはどう ?
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 = "A"
名前の変更時に変数が含まれない場合があります。特に、対応するクラスの外にある場合は、手動で行うときに忘れることもあります。IntelliJは、実際にはクラス名の出現を文字列またはコメントでも検出しますが、これは単なるテキスト検索であり(構文ツリーを調べるのではなく)、したがって誤検出が含まれます。
これは、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)
);
}
switch文を書くことはできませんが、特定のタイプごとに特定の処理に分岐することは可能です。これを行う1つの方法は、標準の二重ディスパッチメカニズムを使用することです。タイプに基づいて「切り替え」たい例は、多数の例外をエラー応答にマップする必要があるジャージー例外マッパーです。この特定のケースではおそらくより良い方法があります(つまり、各例外をエラー応答に変換する多態的なメソッドを使用する)が、ダブルディスパッチメカニズムを使用することは依然として有用で実用的です。
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モデリングフレームワークには、継承も考慮した興味深いアイデアがあります。基本的な概念は、Switch インターフェースで定義されています。切り替えは、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
これは、Javaなどの言語で制御フローを実装するための間違った方法です。