私の理解では、Javaでインターフェースを実装する場合、そのインターフェースで指定されたメソッドは、そのインターフェースを実装するサブクラスで使用する必要があります。
Collectionインターフェースなどの一部のインターフェースには、オプションとしてコメント化されているメソッドがあることに気づきましたが、これは正確にはどういう意味ですか?インターフェイスで指定されたすべてのメソッドが必要だと思ったので、少しスローされましたか?
私の理解では、Javaでインターフェースを実装する場合、そのインターフェースで指定されたメソッドは、そのインターフェースを実装するサブクラスで使用する必要があります。
Collectionインターフェースなどの一部のインターフェースには、オプションとしてコメント化されているメソッドがあることに気づきましたが、これは正確にはどういう意味ですか?インターフェイスで指定されたすべてのメソッドが必要だと思ったので、少しスローされましたか?
回答:
ここでの回答には、非常に多くの混乱があるようです。
Java言語では、インターフェースのすべてのメソッドが、そのインターフェースのすべての実装によって実装される必要があります。限目。このルールに例外はありません。「コレクションは例外です」と言うことは、ここで実際に何が起こっているかについて非常にあいまいな理解を示唆しています。
インターフェースへの準拠には次の2つのレベルがあることを理解することが重要です。
Java言語がチェックできるもの。これはかなりだけに沸く:あり、いくつかの方法のそれぞれのための実装は?
実際に契約を履行。つまり、実装は、インターフェイスのドキュメントにあるべきことを実行しますか?
よく書かれたインターフェースには、実装から期待されることを正確に説明するドキュメントが含まれます。コンパイラはこれをチェックできません。あなたはドキュメントを読んで、彼らが言うことをする必要があります。コントラクトが言っていることを行わない場合、コンパイラに関する限り、インターフェイスの実装がありますが、これは欠陥/無効な実装になります。
コレクションAPIを設計する際、Joshua Blochは、コレクションのさまざまなバリアント(たとえば、読み取り、書き込み、ランダムアクセスなど)を区別するために非常にきめ細かいインターフェースを用意する代わりに、主に非常に大まかなインターフェースのセットのみを使用することにしました。
Collection
、List
、Set
とMap
し、「オプション」などの特定の操作を文書化。これは、きめの細かいインターフェースから生じる組み合わせの爆発を回避するためでした。以下からのJavaコレクションAPIの設計に関するFAQ:
問題を詳細に説明するために、変更可能性の概念を階層に追加するとします。ModifiableCollection、ModifiableSet、ModifiableList、ModifiableMapの4つの新しいインターフェースが必要です。以前は単純な階層であったものが、今では乱雑な階層になっています。また、削除操作を含まない、変更不可能なコレクションで使用する新しいIteratorインターフェースが必要です。UnsupportedOperationExceptionを廃止できますか?残念ながら違います。
配列について考えてみましょう。それらはほとんどのList操作を実装しますが、削除と追加は行いません。これらは「固定サイズ」のリストです。この概念を階層に取り込む場合は、VariableSizeListとVariableSizeMapの2つの新しいインターフェイスを追加する必要があります。VariableSizeCollectionおよびVariableSizeSetは、ModifiableCollectionおよびModifiableSetと同一であるため、追加する必要はありませんが、一貫性を保つためにとにかく追加することもできます。また、変更できないListに対応するには、追加および削除操作をサポートしていない新しいListIteratorが必要です。これで、元の4つではなく、最大10または12のインターフェイスに加えて、2つの新しいIteratorインターフェイスができました。できましたか?番号。
ログ(エラーログ、監査ログ、回復可能なデータオブジェクトのジャーナルなど)を検討します。これらは自然な追加のみのシーケンスであり、削除と設定(置換)を除くすべてのList操作をサポートします。新しいコアインターフェースと新しいイテレータが必要です。
そして、変更不可能なコレクションとは対照的に、不変のコレクションはどうですか?(つまり、クライアントが変更できず、他の理由で変更されることのないコレクション)。同期を必要とせずに複数のスレッドがコレクションに同時にアクセスできるため、これがすべての中で最も重要な違いであると多くの人が主張しています。このサポートを型階層に追加するには、さらに4つのインターフェースが必要です。
現在、最大20のインターフェイスと5つのイテレータがあり、どのインターフェイスにも完全には適合しないコレクションが実際に発生していることはほぼ確実です。たとえば、Mapによって返されるコレクションビューは、削除専用の自然なコレクションです。また、特定の要素をその値に基づいて拒否するコレクションもあります。そのため、実行時の例外を排除していません。
すべてが言われ終わったとき、ランタイム例外をスローする可能性のある非常に小さなコアインターフェースのセットを提供することによって問題全体を回避することは、エンジニアリングの妥協であると感じました。
コレクションAPIのメソッドが「オプションの操作」であると記載されている場合、それはメソッドの実装を実装から除外できるという意味でも、空のメソッド本体を使用できるという意味でもありません(1つには、結果を返す必要があります)。むしろ、有効な実装の選択肢(まだコントラクトに準拠しているもの)はをスローすることUnsupportedOperationException
です。
UnsupportedOperationException
はRuntimeException
、コンパイラに関する限り、どのメソッド実装からでもスローできることに注意してください。たとえば、の実装からスローすることができますCollection.size()
。ただし、そのような実装は、のドキュメントにCollection.size()
これが許可されていると記載されていないため、契約に違反します。
余談ですが、JavaのコレクションAPIで使用されているアプローチは、議論の余地があります(ただし、最初に導入されたときよりも少ないでしょう)。完璧な世界では、インタフェースは考えていないオプションの操作を持っており、きめ細かいインターフェースが代わりに使用されます。問題は、Javaが推論された構造型も交差型もサポートしていないことです。そのため、コレクションの場合、「正しい方法」で処理しようとすると、非常に扱いにくくなります。
There are no exceptions to this rule
。この回答が承認済みとしてマークされないのはなぜですか。他は良いですが、あなたは十分以上のものを与えました。
Foo
実装しないcreate 。次に、上書きしないクラスを作成します。間接的ではありますが、このメソッドはまだ実装されています。同様に、デフォルトのメソッド実装はまだ実装です。Runnable
void run()
Bar
extends Foo
implements Runnable
run
remove
ではデフォルトの実装が提供されていたためです。それを実装しない場合、クラスはデフォルトの実装を取得します。あなたが言及する他の2つのメソッドには、デフォルトの実装がありません。
インターフェイスの実装(非抽象)クラスをコンパイルするには、すべてのメソッドを実装する必要があります。
ただし、その実装が「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が返される場合があります。一部の実装は前者の動作を示し、一部の実装は後者を示します。より一般的には、完了してもコレクションに不適格な要素が挿入されない不適格な要素に対して操作を試行すると、実装のオプションで例外がスローされるか、成功する場合があります。そのような例外は「オプション」としてマークされています
new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }
。
実際、私は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を実装する」だけです。
たわごと英語でごめんなさい。
Java 8以降では、この質問への回答はまだ有効ですが、現在はより微妙になっています。
まず、受け入れられた回答からのこれらのステートメントは正しいままです。
では、Java 8の新しいニュアンスは何でしょうか?「オプションのメソッド」といえば、次のいずれかが適しています。
1.実装が契約上オプションであるメソッド
「3番目のステートメント」では、抽象インターフェースメソッドを常に実装する必要があり、これはJava 8以降でも当てはまります。ただし、Javaコレクションフレームワークの場合と同様に、一部の抽象インターフェイスメソッドをコントラクトで「オプション」として記述することができます。
この場合、インターフェースを実装している作成者は、メソッドを実装しないことを選択できます。ただし、コンパイラーは実装を要求するため、作成者はこのコードを、特定の実装クラスでは不要なオプションのメソッドに使用します。
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の結果をクライアントコードに返すメカニズムを提供します。
すべてのコレクション実装の祖先クラスであるgrepCode のAbstractCollection.javaのコードを確認すると、オプションのメソッドの意味を理解するのに役立ちます。以下は、AbstractCollectionクラスのadd(e)メソッドのコードです。add(e)メソッドは、コレクションインターフェースによるとオプションです。
public boolean add(E e) {
throw new UnsupportedOperationException();
}
オプションのメソッドは、祖先クラスにすでに実装されており、呼び出し時にUnsupportedOperationExceptionをスローすることを意味します。コレクションを変更可能にする場合は、コレクションインターフェイスのオプションメソッドをオーバーライドする必要があります。
ええと、このトピックは...ええ..に向けられていますが、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!
すべての組み合わせが可能です。「デフォルト」のあるものは実装できますが、実装してはいけません。実装しないものは実装する必要があります。
私が今答えるのは完全に間違っていないことを願っています。
みんな良い一日を!
[edit1]:注意:これはJava 8でのみ機能します。
コールバックインターフェイスを実装する方法を探していたため、コールバックごとにすべてのメソッドを実装したくなかったため、オプションのメソッドを実装する必要がありました。
したがって、インターフェイスを使用する代わりに、次のような空の実装を持つクラスを使用しました。
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の時点でインターフェースにデフォルトのメソッドを追加することが実際に実行可能であることは注目に値します。default
インタフェースのメソッドシグネチャに配置されたキーワードは、メソッドをオーバーライドするオプションを持つクラスになりますが、それを必要としません。
コアコレクションインターフェースの数を管理しやすいように保つために、Javaプラットフォームは、各コレクションタイプのバリアントごとに個別のインターフェースを提供していません。(そのようなバリアントには、不変、固定サイズ、追加のみが含まれる場合があります。)代わりに、各インターフェースの変更操作はオプションとして指定されます—特定の実装では、すべての操作をサポートしないことを選択できます。サポートされていない操作が呼び出されると、コレクションはUnsupportedOperationExceptionをスローします。実装は、サポートするオプションのオペレーションのどれを文書化する必要があります。Javaプラットフォームのすべての汎用実装は、すべてのオプション操作をサポートしています。