タスクは、Java enum
:を使用して美しい戦略設計パターンを実装することです。
public enum MyEnum {
FIRST {
@Override
public String doIt() {
return "1: " + someField; //error
}
},
SECOND {
@Override
public String doIt() {
return "2: " + someField; //error
}
};
private String someField;
public abstract String doIt();
}
しかし、someField
を参照すると、
非静的フィールドsomeFieldへの静的参照を作成できません。
何が問題であり、それをより良くすることは可能ですか?
特殊なenum
は、内部クラスのセマンティクスを持つサブクラスに他なりません。コンパイル後にバイトコードを見ると、コンパイラはプライベートフィールドを読み取るためのアクセサメソッドのみを挿入しますが、特殊な列挙型は独自のクラスとしてコンパイルされていることがわかります。 enum
は次のように実装されていると考えることができます。
public abstract class MyEnum {
private static class First extends MyEnum {
@Override
public String doIt() {
return "1: " + someField; //error
}
}
private static class Second extends MyEnum {
@Override
public String doIt() {
return "2: " + someField; //error
}
}
public static final MyEnum FIRST = new First();
public static final MyEnum SECOND = new Second();
private String someField;
public abstract String doIt();
}
ご覧のとおり、同じコンパイラエラーが発生します。事実上、問題はenum
sではなく、それらの内部クラスのセマンティクスに関連しています。
しかし、コンパイラがコードの意図を推測し、意図したことが違法であることを警告しようとする境界的なケースを見つけました。一般に、someField
フィールドは、特殊なenum
に表示されます。ただし、内部クラスからprivate
フィールドにアクセスする方法は2つあり、有効な方法は1つだけです。
private
メンバーは継承されません。したがって、スーパークラスで定義されている場合、private
インスタンスからthis
フィールドにアクセスすることはできません。
内部クラスの場合、外部クラスのメンバーは、private
であってもアクセスできます。これは、アクセサメソッドによってprivate
フィールドを公開する外部クラスにアクセサメソッドを挿入することにより、コンパイラによって実現されます。非static
フィールドにアクセスできるのは、内部クラスが非static
の場合のみです。ただし、enum
sの場合、内部クラスは常にstatic
です。
後者の条件は、コンパイラが文句を言うものです。
非静的フィールドへの静的参照を作成できません
someField
static
内部クラスから非static
フィールドにアクセスしようとしています。内部クラスのセマンティクスのためにフィールドが技術的に表示される場合でも、これは不可能です。たとえば、スーパークラスから値を読み取ることにより、値にアクセスするようにコンパイラに明示的に指示できます。
public String doIt() {
MyEnum thiz = this;
return thiz.someField;
}
これで、コンパイラは、(静的ではない)外部クラスインスタンス(存在しない)のsomeField
フィールドに誤ってアクセスするのではなく、表示されている(外部)タイプのメンバーにアクセスしようとしていることを認識します。 (同様に、継承チェーンを下って外部インスタンスのフィールドにアクセスしないという同じ考えを表すsuper.someField
を記述できます。)ただし、より簡単な解決策は、フィールドをprotected
にすることです。このようにして、コンパイラーは継承の可視性に満足し、元のセットアップをコンパイルします。
プライベートではなくsomeField
を保護するか、代わりにsuper.someField
を使用すると、アクセスできるようになります。
someField
はプライベートです。プライベート修飾子を削除するか、抽象クラスに移動してください。
プライベートフィールドには、サブクラスからアクセスできません。これは、インスタンスごとにMyEnum.doIt()
抽象メソッドを実装するときに行うこととまったく同じです。 protected
に変更すると、機能します。
あなたができることは次のとおりです。
_public enum MyEnum {
FIRST,SECOND;
private String someField;
public String doIt(){
switch(this){
case FIRST: return "1: " + someField; break;
case SECOND: return "2: " + someField; break;
}
}
}
_
このように、あなたはまだEnum
を継承し、MyEnum.values()
とEnum
の派生から来る他の特典を使うことができます。
どうやら問題はあなたが言うときそれです:
public enum MyEnum {
...
public abstract String doIt();
}
実装を提供する必要があるため、列挙型はabstract
"class"である必要があります。したがって、あなたが言うとき
FIRST {
@Override
public String doIt() {
return "1: " + this.someField; //error
}
}
「基本クラス」MyEnum
のプライベートフィールドにアクセスしようとしているため、エラーが発生します。プライベートフィールドであるため、暗黙的に作成された匿名サブクラスからは表示されません。そのため、protected
はサブクラスから表示されるため、問題が修正されます。
この問題について話しているStackOverflowに関するいくつかの質問があります。たとえば、 シングルトン、列挙型、匿名の内部クラス または なぜ匿名で列挙型をサブクラス化できますが、最終クラスではないのですか? 。
編集:明らかに、このステートメントのすべてが正しいわけではありません。フィールドがサブクラスから見えないため、this.someField
は機能しませんが、issuper.someField
として表示されます。これは私が今まで見たことのない現象であり、これから調べてみます。
列挙型が静的変数の場合、someFieldはプライベート変数です。この方法で非静的変数を静的変数に割り当てることはできません。
列挙型を使用してStrategyパターンを実装することはありません。すべてのコードは同じunti(ファイル)になります。
アイデアは、コードを分離することです。インターフェイスを基本クラスとして使用し、各ストラテジーを個別のサブクラスとして実装します。きれいでいい。