Javaインターフェイスのオプションメソッド


120

私の理解では、Javaでインターフェースを実装する場合、そのインターフェースで指定されたメソッドは、そのインターフェースを実装するサブクラスで使用する必要があります。

Collectionインターフェースなどの一部のインターフェースには、オプションとしてコメント化されているメソッドがあることに気づきましたが、これは正確にはどういう意味ですか?インターフェイスで指定されたすべてのメソッドが必要だと思ったので、少しスローされましたか?


どの方法を参照していますか?JavaDocまたはソースコードでそれが見つかりません
dcpomero '13


回答:


232

ここでの回答には、非常に多くの混乱があるようです。

Java言語では、インターフェースのすべてのメソッドが、そのインターフェースのすべての実装によって実装される必要があります。限目。このルールに例外はありません。「コレクションは例外です」と言うことは、ここで実際に何が起こっているかについて非常にあいまいな理解を示唆しています。

インターフェースへの準拠には次の2つのレベルがあることを理解することが重要です。

  1. Java言語がチェックできるもの。これはかなりだけに沸く:あり、いくつかの方法のそれぞれのための実装は?

  2. 実際に契約を履行。つまり、実装は、インターフェイスのドキュメントにあるべきことを実行しますか?

    よく書かれたインターフェースには、実装から期待されることを正確に説明するドキュメントが含まれます。コンパイラはこれをチェックできません。あなたはドキュメントを読んで、彼らが言うことをする必要があります。コントラクトが言っていることを行わない場合、コンパイラに関する限り、インターフェイスの実装がありますが、これは欠陥/無効な実装になります。

コレクションAPIを設計する際、Joshua Blochは、コレクションのさまざまなバリアント(たとえば、読み取り、書き込み、ランダムアクセスなど)を区別するために非常にきめ細かいインターフェースを用意する代わりに、主に非常に大まかなインターフェースのセットのみを使用することにしました。 CollectionListSetMapし、「オプション」などの特定の操作を文書化。これは、きめの細かいインターフェースから生じる組み合わせの爆発を回避するためでした。以下からの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です。

UnsupportedOperationExceptionRuntimeException、コンパイラに関する限り、どのメソッド実装からでもスローできることに注意してください。たとえば、の実装からスローすることができますCollection.size()。ただし、そのような実装は、のドキュメントにCollection.size()これが許可されていると記載されていないため、契約に違反します。

余談ですが、JavaのコレクションAPIで使用されているアプローチは、議論の余地があります(ただし、最初に導入されたときよりも少ないでしょう)。完璧な世界では、インタフェースは考えていないオプションの操作を持っており、きめ細かいインターフェースが代わりに使用されます。問題は、Javaが推論された構造型も交差型もサポートしていないことです。そのため、コレクションの場合、「正しい方法」で処理しようとすると、非常に扱いにくくなります。


30
の+1 There are no exceptions to this rule。この回答が承認済みとしてマークされないのはなぜですか。他は良いですが、あなたは十分以上のものを与えました。
xyz

9
「Java言語では、インターフェースのすべてのメソッドが、そのインターフェースのすべての実装によって実装されている必要があります。ピリオド。このルールに例外はありません。」例外...あるとき。:-) Java 8インターフェースは、デフォルトのメソッド実装を指定できます。したがって、Java 8では、インターフェースのすべてのメソッドが、インターフェースのすべての実装によって実装されている必要があります。少なくとも、 conreteクラスで実装をコーディングします。
DaBlick 2014

1
@DaBlick「すべての実装によって実装される」と言ったとき、メソッドの実装が実装クラスのソースに存在する必要があることを意味していませんでした。Java 8より前でも、インターフェースを実装していないクラスからでも、インターフェースメソッドの実装を継承できます。例:publicメソッドでFoo実装しないcreate 。次に、上書きしないクラスを作成します。間接的ではありますが、このメソッドはまだ実装されています。同様に、デフォルトのメソッド実装はまだ実装です。Runnablevoid run()Barextends Fooimplements Runnablerun
ローレンスゴンサルベス2014

謝罪。私は、元の投稿に関連するかもしれないJava 8の機能に注意を引くほど、徹底的に批判的になることを試みていませんでした。Java 8では、スーパークラスでもサブクラスでもコード化されていない実装を選択できるようになりました。これ(IMHO)は、複数継承の禁止がいくつかの課題を提示した可能性がある場合に適切である可能性があるものを含む、設計パターンの新しい世界を開きます。これにより、非常に便利なデザインパターンの新しいセットが生成されると思います。\
DaBlick

3
@AndrewSは、Java 8 removeではデフォルトの実装が提供されていたためです。それを実装しない場合、クラスはデフォルトの実装を取得します。あなたが言及する他の2つのメソッドには、デフォルトの実装がありません。
ローレンスゴンサルベス

27

インターフェイスの実装(非抽象)クラスをコンパイルするには、すべてのメソッドを実装する必要があります。

ただし、その実装が「Collectionインターフェースの一部のメソッドのように」「実装されていない」単純な例外スローであると考える場合、Collectionインターフェースはこの場合の例外であり、通常の場合ではありません。通常、実装クラスはすべてのメソッドを実装する必要があります(実装します)。

コレクションの「オプション」とは、実装クラスが(上記の用語に従って)「実装」する必要がないことを意味し、単にスローするだけです NotSupportedException)。

良い例add()-不変コレクションのメソッド-具象は、投げるだけのメソッドを実装するだけですNotSupportedException

Collectionそれが乱雑な継承ツリーを防ぐために行われる場合、それはプログラマーを悲惨にします-しかし、ほとんどの場合、このパラダイムは助言されず、可能であれば避けられるべきです。


更新:

Java 8以降、デフォルトのメソッドが導入されました。

つまり、インターフェースは、その実装を含め、メソッドを定義できます。
これは、新しい機能を必要としないコードの一部に対する下位互換性をサポートしながら、インターフェイスに機能を追加できるようにするために追加されました。

メソッドは、それを宣言するすべてのクラスによって依然として実装されていますが、インターフェイスの定義を使用していることに注意してください。


「めちゃくちゃにならない」というよりは、「それがまさにそれだ」ということだと思います。

@pst:設計者が最初にそれを実装したときに何を考えていたと思いますが、私はそれを確実に知る方法がありません。私は考えて何が違うのアプローチは、単に混乱を作成することになりますが、再び-間違っている可能性があります。ここで私が示したポイントは次のとおりです。この例は例外であり、通常ではありません-場合によっては役立つかもしれませんが、一般的なケースでは-可能であれば回避する必要があります。
アミット

12
「実装する必要はありません(おそらくスローするメソッドを作成するだけです...)」。それメソッドを実装しています。
ローン侯爵

1
残念ながらこれは受け入れられた答えなので、書き直すことをお勧めします。EJPがすでに指摘しているように、「通常、実装クラスはすべてのメソッドを実装する必要があります(実装します)」は誤解を招きます。
アルベルト

2
実装クラスがそれを実装する必要がないことを収集手段で「オプション」「 -これはプレーンfalseです。 『』他の平均何かをあなたを実装する必要はありません。。
djechlin

19

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例外がスローされる場合が あります。


私はあなたに私の友人にキスをすることができました。
マイクロ

16

Collectionインターフェースのオプションのメソッドは、メソッドの実装が例外をスローすることを許可されていますが、とにかく実装する必要があることを意味します。ドキュメントで指定されているように

一部のコレクション実装には、それらに含まれる可能性のある要素に制限があります。たとえば、一部の実装ではnull要素を禁止し、一部の実装では要素のタイプに制限があります。不適格な要素を追加しようとすると、チェックされていない例外(通常はNullPointerExceptionまたはClassCastException)がスローされます。不適格な要素の存在を照会しようとすると、例外がスローされるか、単にfalseが返される場合があります。一部の実装は前者の動作を示し、一部の実装は後者を示します。より一般的には、完了してもコレクションに不適格な要素が挿入されない不適格な要素に対して操作を試行すると、実装のオプションで例外がスローされるか、成功する場合があります。そのような例外は「オプション」としてマークされています


javadocsがオプションで何を意味するのか本当に理解できませんでした。あなたが言った通りの意味だと思います。ただし、ほとんどのメソッドはその標準ではオプションです new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }
エモリー

これはオプションのメソッドには当てはまらないようですが、たとえば、add((T)null)あるケースでは有効であるが別のケースでは有効でない場合があります。つまり、これはオプションの例外/動作と引数(「要素の制限」...「対象外の要素」...「オプションとしてマークされた例外」)について話し、オプションのメソッドについては触れていません。

9

すべてのメソッドは、コードをコンパイルするために実装する必要があります(defaultJava 8+ での実装を除く)。ただし、実装は機能的に有用なことを何もする必要はありません。具体的には、次のとおりです。

  • 空白の場合があります(空のメソッド)。
  • UnsupportedOperationException(または同様のもの)を投げるだけかもしれません

後者のアプローチは、多くの場合、コレクションクラスで使用されます。すべてのメソッドはまだ実装されていますが、実行時に呼び出されると例外がスローされる場合があります。


5

実際、私は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を実装する」だけです。

たわごと英語でごめんなさい。


5

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の結果をクライアントコードに返すメカニズムを提供します。


4

すべてのコレクション実装の祖先クラスであるgrepCode のAbstractCollection.javaのコードを確認すると、オプションのメソッドの意味を理解するのに役立ちます。以下は、AbstractCollectionクラスのadd(e)メソッドのコードです。add(e)メソッドは、コレクションインターフェースによるとオプションです。

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

オプションのメソッドは、祖先クラスにすでに実装されており、呼び出し時にUnsupportedOperationExceptionをスローすることを意味します。コレクションを変更可能にする場合は、コレクションインターフェイスのオプションメソッドをオーバーライドする必要があります。


4

ええと、このトピックは...ええ..に向けられていますが、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でのみ機能します。


はい、申し訳ありません。これについて言及するのを忘れていました...今すぐ編集する必要があります。
Thorben Kuck

1

コールバックインターフェイスを実装する方法を探していたため、コールバックごとにすべてのメソッドを実装したくなかったため、オプションのメソッドを実装する必要がありました。

したがって、インターフェイスを使用する代わりに、次のような空の実装を持つクラスを使用しました。

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);
}

このように、コールバックごとにすべてのメソッドを実装することを心配する必要はありませんが、必要なものだけをオーバーライドします。



0

OracleのJavaコレクションチュートリアル:

コアコレクションインターフェースの数を管理しやすいように保つために、Javaプラットフォームは、各コレクションタイプのバリアントごとに個別のインターフェースを提供していません。(そのようなバリアントには、不変、固定サイズ、追加のみが含まれる場合があります。)代わりに、各インターフェースの変更操作はオプションとして指定されます—特定の実装では、すべての操作をサポートしないことを選択できます。サポートされていない操作が呼び出されると、コレクションはUnsupportedOperationExceptionをスローします。実装は、サポートするオプションのオペレーションのどれを文書化する必要があります。Javaプラットフォームのすべての汎用実装は、すべてのオプション操作をサポートしています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.