Javaでインターフェースを実装する場合、私の理解では、そのインターフェースで指定されたメソッドは、そのインターフェースを実装するサブクラスで使用する必要があります。
Collection インターフェイスなどの一部のインターフェイスには、オプションとしてコメントされているメソッドがあることに気付きましたが、これは正確にはどういう意味ですか? インターフェイスで指定されたすべてのメソッドが必要になると思っていたので、少し戸惑いましたか?
ここでの回答には非常に多くの混乱があるようです。
Java 言語では、インターフェース内のすべてのメソッドが、そのインターフェースのすべての実装によって実装される必要があります。限目。この規則に例外はありません。「コレクションは例外です」と言うのは、ここで実際に何が起こっているかについて非常にあいまいな理解を示唆しています。
インターフェイスへの準拠には、2 つのレベルがあることを認識することが重要です。
Java言語がチェックできるもの。これは要するに、各メソッドの実装はあるのでしょうか?
実際に契約を履行します。つまり、実装は、インターフェースのドキュメントがそうすべきだと言っていることをしますか?
適切に記述されたインターフェースには、実装に期待されることを正確に説明するドキュメントが含まれます。コンパイラはこれをチェックできません。ドキュメントを読んで、彼らの言うことを実行する必要があります。コントラクトに記載されていることを行わない場合、コンパイラに関する限り、インターフェイスの実装がありますが、それは欠陥のある/無効な実装になります。
コレクション API を設計するとき、Joshua Bloch は、コレクションのさまざまなバリアント (たとえば、読み取り可能、書き込み可能、ランダム アクセスなど) を区別するための非常に細かいインターフェイスを使用する代わりに、非常に粗いインターフェイス セットのみを使用することを決定しました。
Collection
、List
、Set
およびMap
、そして特定の操作を「オプション」として文書化します。これは、きめの細かいインターフェイスに起因する組み合わせ爆発を回避するためでした。Java Collections API Design FAQから:
問題を詳細に説明するために、Hierarchy に変更可能性の概念を追加するとします。ModifiableCollection、ModifiableSet、ModifiableList、ModifiableMap の 4 つの新しいインターフェイスが必要です。以前は単純なヒエラルキーだったものが、今では乱雑なヘテラーキーになっています。また、削除操作を含まない、変更不可能なコレクションで使用するための新しい Iterator インターフェイスが必要です。UnsupportedOperationException を廃止できますか? 残念ながら違います。
配列を検討してください。リスト操作のほとんどを実装しますが、削除と追加は実装しません。それらは「固定サイズ」のリストです。この概念を階層に取り込みたい場合は、VariableSizeList と VariableSizeMap という 2 つの新しいインターフェイスを追加する必要があります。VariableSizeCollection と VariableSizeSet は ModifiableCollection と ModifiableSet と同じであるため追加する必要はありませんが、一貫性を保つために追加することを選択することもできます。また、変更不可能なリストに対応するには、追加操作と削除操作をサポートしない新しい種類の ListIterator が必要です。今では、元の 4 つの代わりに、最大 10 または 12 のインターフェイスに加えて、2 つの新しい Iterator インターフェイスがあります。終わりましたか?いいえ。
ログ (エラー ログ、監査ログ、回復可能なデータ オブジェクトのジャーナルなど) を検討してください。これらは自然な追加のみのシーケンスであり、削除と設定 (置換) を除くすべてのリスト操作をサポートします。新しいコア インターフェイスと新しい反復子が必要です。
また、変更不可能なコレクションとは対照的に、不変のコレクションはどうでしょうか? (つまり、クライアントが変更できず、他の理由で変更されることのないコレクション)。複数のスレッドが同期を必要とせずにコレクションに同時にアクセスできるため、これが最も重要な違いであると多くの人が主張しています。このサポートを型階層に追加するには、さらに 4 つのインターフェイスが必要です。
現在、最大で 20 ほどのインターフェイスと 5 つのイテレータがあり、実際にはどのインターフェイスにもうまく収まらないコレクションが発生していることはほぼ確実です。たとえば、Map によって返されるコレクション ビューは、自然な削除専用コレクションです。また、値に基づいて特定の要素を拒否するコレクションもあるため、実行時例外はまだ廃止されていません。
結論から言うと、実行時例外をスローできるコア インターフェイスの非常に小さなセットを提供することで、問題全体を回避することは、適切なエンジニアリング上の妥協点であると感じました。
Collections API のメソッドが「オプションの操作」であると文書化されている場合、実装でメソッドの実装を省略できるという意味でも、空のメソッド本体を使用できるという意味でもありません (1 つの理由として、多くの結果を返す必要があります)。むしろ、有効な実装の選択 (コントラクトに引き続き準拠するもの) は、 をスローすることであることを意味しますUnsupportedOperationException
。
UnsupportedOperationException
はであるためRuntimeException
、コンパイラに関する限り、任意のメソッド実装からスローできることに注意してください。たとえば、 の実装からスローできますCollection.size()
。Collection.size()
ただし、ドキュメントにはこれが許可されているとは記載されていないため、そのような実装は契約に違反します。
余談ですが、Java の Collections API で使用されているアプローチは、やや物議を醸しています (ただし、最初に導入されたときよりも、現在の方が少ない可能性があります)。完璧な世界では、インターフェイスにはオプションの操作がなく、代わりにきめ細かいインターフェイスが使用されます。問題は、Java が推論された構造型も交差型もサポートしていないことです。そのため、コレクションの場合、「正しい方法」で処理しようとすると非常に扱いにくくなります。
インターフェイスの実装 (非抽象) クラスをコンパイルするには、すべてのメソッドを実装する必要があります。
ただし、その実装が単純な例外スローであるメソッドを「実装されていない」(Collection
インターフェイスの一部のメソッドのように)と考えるとCollection
、この場合、インターフェイスは例外であり、通常のケースではありません。通常、実装クラスはすべてのメソッドを実装する必要があります (実装する予定です)。
コレクションの「オプション」とは、実装するクラスが (上記の用語に従って) それを「実装」する必要がなく、単に をスローすることを意味します NotSupportedException
)。
良い例add()
- 不変コレクションのメソッド - 具象は、スローするだけのメソッドを実装するだけですNotSupportedException
Collection
継承ツリーが乱雑になるのを防ぐために行われる場合、プログラマーは惨めになりますが、ほとんどの場合、このパラダイムは推奨されておらず、可能であれば回避する必要があります。
アップデート:
Java 8 以降、デフォルトのメソッドが導入されました。
つまり、インターフェイスはメソッドを定義できます-その実装を含みます。
これは、新しい機能を必要としないコードの下位互換性をサポートしながら、インターフェイスに機能を追加できるようにするために追加されました。
メソッドは、それを宣言するすべてのクラスによって引き続き実装されますが、インターフェイスの定義を使用することに注意してください。
Java のインターフェースは、クラスを実装するための規約を宣言するだけです。そのインターフェイスのすべてのメソッドを実装する必要がありますが、実装するクラスはそれらを未実装、つまり空白のままにしておくことができます。わざとらしい例として、
interface Foo {
void doSomething();
void doSomethingElse();
}
class MyClass implements Foo {
public void doSomething() {
/* All of my code goes here */
}
public void doSomethingElse() {
// I leave this unimplemented
}
}
今はdoSomethingElse()
未実装のままにして、サブクラスが自由に実装できるようにしています。それはオプションです。
class SubClass extends MyClass {
@Override
public void doSomethingElse() {
// Here's my implementation.
}
}
ただし、コレクション インターフェイスについて話している場合、他の人が言っているように、それらは例外です。特定のメソッドが実装されていない状態でそれらを呼び出すと、 UnsupportedOperationException
例外がスローされる場合があります。
Collection インターフェイスのオプションのメソッドは、メソッドの実装で例外をスローできることを意味しますが、とにかく実装する必要があります。ドキュメントで指定されているとおり:
一部のコレクションの実装には、含まれる要素に制限があります。たとえば、null 要素を禁止する実装もあれば、要素の型に制限がある実装もあります。不適格な要素を追加しようとすると、未チェックの例外 (通常は NullPointerException または ClassCastException) がスローされます。不適格な要素の存在を照会しようとすると、例外がスローされるか、単に false が返される場合があります。一部の実装では前者の動作が示され、一部の実装では後者の動作が示されます。より一般的には、完了しても不適格な要素がコレクションに挿入されない不適格な要素に対して操作を試行すると、実装のオプションで例外がスローされるか、成功する可能性があります。そのような例外は「オプション」としてマークされています
コードをコンパイルするにはすべてのメソッドを実装する必要がありますが ( default
Java 8+ で実装されているものを除く)、実装は機能的に有用なことを行う必要はありません。具体的には、次のとおりです。
UnsupportedOperationException
(または同様の)を投げるだけかもしれません後者のアプローチは、多くの場合、コレクション クラスで使用されます。すべてのメソッドは引き続き実装されますが、一部のメソッドは実行時に呼び出されると例外をスローする場合があります。
Java 8 以降では、この質問に対する答えは引き続き有効ですが、より微妙になっています。
まず、受け入れられた回答からのこれらのステートメントは正しいままです。
では、Java 8 の新しいニュアンスは何ですか? 「オプションの方法」について言えば、次のいずれかが適切です。
1. 実装が契約上オプションであるメソッド
「3 番目のステートメント」は、抽象インターフェイス メソッドを常に実装する必要があることを示しており、これは Java 8 以降でも当てはまります。ただし、Java Collections Framework と同様に、一部の抽象インターフェイス メソッドを契約で「オプション」として記述することができます。
この場合、インターフェースを実装している作成者は、メソッドを実装しないことを選択できます。ただし、コンパイラは実装を要求するため、作成者は特定の実装クラスで不要な任意のメソッドにこのコードを使用します。
public SomeReturnType optionalInterfaceMethodA(...) {
throw new UnsupportedOperationException();
}
Java 7 以前では、これが実際に存在する唯一の「オプション メソッド」でした。つまり、実装されていない場合、UnsupportedOperationException をスローするメソッドでした。この動作は、必ずインターフェイス コントラクトによって指定されます (たとえば、Java Collections Framework のオプションのインターフェイス メソッド)。
2. 再実装がオプションのデフォルト メソッド
Java 8 では、デフォルト メソッドの概念が導入されました。これらは、実装が可能であり、インターフェイス定義自体によって提供されるメソッドです。通常、デフォルト メソッドを提供できるのは、メソッド本体が他のインターフェイス メソッド (つまり、「プリミティブ」) を使用して記述できる場合、およびthis
「クラスがこのインターフェイスを実装したこのオブジェクト」を意味する場合のみです。
既定のメソッドは、インターフェイスのコントラクトを満たす必要があります (他のインターフェイス メソッドの実装と同様に)。したがって、実装クラスでインターフェイス メソッドの実装を指定することは、作成者の裁量に任されています (動作が目的に適している限り)。
この新しい環境では、Java Collections Frameworkを次のように書き直すことができます。
public interface List<E> {
:
:
default public boolean add(E element) {
throw new UnsupportedOperationException();
}
:
:
}
このように、「オプションの」メソッドadd()
は、実装するクラスが独自の新しい動作を提供しない場合、UnsupportedOperationException をスローするデフォルトの動作を持ちます。これは、まさにあなたが望んでいることであり、List の契約に準拠しています。List 実装に新しい要素を追加することを許可しないクラスを作成者が作成しているadd()
場合、デフォルトの動作がまさに必要なものであるため、 の実装はオプションです。
この場合、メソッドがインターフェイス自体に実装されているため、上記の「3 番目のステートメント」は依然として当てはまります。
3.Optional
結果を返すメソッド
最後の新しい種類のオプション メソッドは、単純に を返すメソッドですOptional
。このOptional
クラスは、結果を処理する明らかによりオブジェクト指向の方法を提供しnull
ます。
新しい Java Streams API を使用してコーディングするときに一般的に見られるようなプログラミングの流暢なスタイルでは、任意の時点で null の結果が発生すると、プログラムは NullPointerException でクラッシュします。このOptional
クラスは、クライアント コードをクラッシュさせることなく流暢なスタイルを有効にする方法で、null の結果をクライアント コードに返すメカニズムを提供します。
実際、私は SurfaceView.Callback2 に触発されています。これが公式のやり方だと思います
public class Foo {
public interface Callback {
public void requiredMethod1();
public void requiredMethod2();
}
public interface CallbackExtended extends Callback {
public void optionalMethod1();
public void optionalMethod2();
}
private Callback mCallback;
}
クラスがオプションのメソッドを実装する必要がない場合は、「コールバックを実装する」だけです。クラスがオプションのメソッドを実装する必要がある場合は、「CallbackExtended を実装する」だけです。
くそ英語でごめんなさい。
さて、このトピックは対処されています...ええ..しかし、考えてみてください、1つの答えがありません。インターフェイスの「デフォルトメソッド」について話しています。たとえば、何か (デストラクタなど) を閉じるためのクラスがあるとします。3つのメソッドが必要だとしましょう。それらを「doFirst()」、「doLast()」、および「onClose()」と呼びましょう。
したがって、そのタイプのオブジェクトは少なくとも「onClose()」を実現する必要があると言えますが、その他はオプションです。
インターフェイスの「デフォルトメソッド」を使用すると、それを実現できます。ほとんどの場合、これはインターフェイスの理由を否定しますが、フレームワークを設計している場合、これは便利です。
したがって、このように実現したい場合は、次のようになります
public interface Closer {
default void doFirst() {
System.out.print("first ... ");
}
void onClose();
default void doLast() {
System.out.println("and finally!");
}
}
たとえば、「Test」というクラスに実装した場合、コンパイラは次のように完全に問題ありません。
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
}
出力で:
first ... closing ... and finally!
また
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
@Override
public void doLast() {
System.out.println("done!");
}
}
出力で:
first ... closing ... done!
すべての組み合わせが可能です。「デフォルト」のあるものはすべて実装できますが、実装してはなりませんが、ないものは実装する必要があります。
私が今答えるのが完全に間違っていないことを願っています。
皆さん、良い一日を!
[編集 1]: 注意: これは Java 8 でのみ機能します。
すべてのコレクション実装の祖先クラスである grepCode 内のAbstractCollection.javaのコードを調べてみると、オプション メソッドの意味を理解するのに役立ちます。AbstractCollection クラスの add(e) メソッドのコードを次に示します。add(e) メソッドは、コレクションインターフェイスに応じてオプションです
public boolean add(E e) {
throw new UnsupportedOperationException();
}
オプションのメソッドは、祖先クラスに既に実装されており、呼び出し時に UnsupportedOperationException をスローすることを意味します。コレクションを変更可能にしたい場合は、コレクション インターフェイスのオプションメソッドをオーバーライドする必要があります。
コールバック インターフェイスを実装する方法を探していたので、コールバックごとにすべてのメソッドを実装したくないため、オプションのメソッドを実装する必要がありました。
そのため、インターフェイスを使用する代わりに、次のような空の実装を持つクラスを使用しました。
public class MyCallBack{
public void didResponseCameBack(String response){}
}
そして、メンバー変数 CallBack を次のように設定できます。
c.setCallBack(new MyCallBack() {
public void didResponseCameBack(String response) {
//your implementation here
}
});
次に、このように呼び出します。
if(mMyCallBack != null) {
mMyCallBack.didResponseCameBack(response);
}
このように、コールバックごとにすべてのメソッドを実装することを心配する必要はありませんが、必要なものだけをオーバーライドします。
OPの質問には答えませんが、Java 8以降、インターフェースにデフォルトメソッドを追加することは実際には doable であることに注意してください。インターフェイスのdefault
メソッド シグネチャに配置されたキーワードにより、メソッドをオーバーライドするオプションを持つクラスが生成されますが、メソッドをオーバーライドする必要はありません。
コア コレクション インターフェースの数を管理可能な状態に保つために、Java プラットフォームでは、各コレクション タイプのバリアントごとに個別のインターフェースを提供していません。(このようなバリアントには、不変、固定サイズ、および追加のみが含まれる場合があります。) 代わりに、各インターフェイスの変更操作はオプションとして指定されています。特定の実装では、すべての操作をサポートしないことを選択する場合があります。サポートされていない操作が呼び出された場合、コレクションはUnsupportedOperationExceptionをスローします。実装は、サポートするオプションの操作を文書化する責任があります。Java プラットフォームの汎用実装はすべて、オプションの操作をすべてサポートしています。