web-dev-qa-db-ja.com

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

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

Collectionインターフェイスなどの一部のインターフェイスには、オプションとしてコメントされるメソッドがありますが、これは正確に何を意味するのでしょうか?インターフェイスで指定されたすべてのメソッドが必要になると思ったので、それは少し私を投げましたか?

106
mjsey

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

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

インターフェイスに準拠する2つのレベルがあることを認識することが重要です。

  1. Java言語で確認できるもの。これは大体次のように要約されます。各メソッドにsome実装がありますか?

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

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

コレクションAPIを設計する際、Joshua Blochは、コレクションのさまざまなバリエーション(読み取り可能、書き込み可能、​​ランダムアクセスなど)を区別する非常にきめの細かいインターフェイスを使用する代わりに、主に非常に粗いインターフェイスセットのみを使用することにしましたCollectionListSet、およびMapを指定し、特定の操作を「オプション」として文書化します。これは、きめの細かいインターフェイスに起因する組み合わせの爆発を避けるためでした。 Java Collections API Design FAQ から:

問題を詳細に説明するために、変更可能性の概念を階層に追加するとします。 ModifiableCollection、ModifiableSet、ModifiableList、ModifiableMapの4つの新しいインターフェイスが必要です。以前は単純な階層でしたが、今では乱雑な階層です。また、削除操作を含まない、変更不可能なコレクションで使用する新しいIteratorインターフェイスが必要です。 UnsupportedOperationExceptionを廃止できますか?残念ながら違います。

配列を検討してください。それらはほとんどのリスト操作を実装しますが、削除および追加はしません。これらは「固定サイズ」リストです。階層内でこの概念をキャプチャする場合、2つの新しいインターフェイスを追加する必要があります:VariableSizeListおよびVariableSizeMap。 VariableSizeCollectionとVariableSizeSetを追加する必要はありません。これらはModifiableCollectionおよびModifiableSetと同一であるためですが、一貫性のために追加することもできます。また、変更不可能なリストに合わせて、追加および削除操作をサポートしない新しい種類のListIteratorが必要です。これで、最大10個または12個のインターフェイスに加えて、元の4個ではなく2個の新しいIteratorインターフェイスになりました。終わった?番号。

ログ(エラーログ、監査ログ、回復可能なデータオブジェクトのジャーナルなど)を検討してください。これらは自然な追加専用シーケンスであり、removeおよびset(replace)を除くすべてのList操作をサポートします。新しいコアインターフェイスと新しいイテレータが必要です。

そして、変更不可能なコレクションとは対照的に、不変のコレクションについてはどうですか? (つまり、クライアントが変更できないコレクションであり、その他の理由で変更されることはありません)。複数のスレッドが同期を必要とせずに同時にコレクションにアクセスできるため、これがすべての最も重要な区別であると多くの人が主張しています。このサポートを型階層に追加するには、さらに4つのインターフェイスが必要です。

現在、最大20個のインターフェイスと5個のイテレータがあり、実際には、どのインターフェイスにもきれいに収まらないコレクションが実際に発生していることはほぼ確実です。たとえば、Mapによって返されるコレクションビューは、削除のみの自然なコレクションです。また、値に基づいて特定の要素を拒否するコレクションがあるため、実行時例外はまだ処理されていません。

すべてのことを言って完了したら、ランタイム例外をスローできるコアインターフェイスの非常に小さなセットを提供することにより、問題全体を回避することは健全なエンジニアリング上の妥協だと感じました。

Collections APIのメソッドが「オプションの操作」として文書化されている場合、メソッドの実装を実装に残しておくことができるということではなく、空のメソッド本体を使用できることも意味しません。結果を返す必要があります)。むしろ、有効な実装の選択(まだ契約に準拠しているもの)が UnsupportedOperationException をスローすることであることを意味します。

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

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

217

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

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

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

良い例-不変コレクションのadd()メソッド-具体的には、NotSupportedExceptionをスローするだけのメソッドを実装します

Collectionの場合、乱雑な継承ツリーを防ぐために行われ、プログラマーを悲惨なものにします-しかし、mostの場合、このパラダイムは推奨されず、可能な場合は避けるべきです。


更新:

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

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

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

26
amit

Javaのインターフェイスは、クラスを実装するためのコントラクトを宣言するだけです。そのインターフェースのすべてのメソッドmustを実装しますが、実装クラスはそれらを未実装、つまり空のままにしておくことができます。考案された例として、

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

17
S.R.I

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

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

16
MByD

コードをコンパイルするには、すべてのメソッドを実装する必要があります(Java 8+のdefault実装を除く)が、実装は機能的に有用なことをする必要はありません。具体的には:

  • 空白の場合があります(空のメソッド)。
  • 単にUnsupportedOperationException(または同様の)をスローする場合があります

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

9
Michael Berry

Java 8以降では、この質問に対する回答はまだ有効ですが、現在ではより微妙になっています。

まず、受け入れられた答えからのこれらのステートメントは正しいままです。

  • インターフェースは、コントラクト(実装クラスが有効と見なされるために従う必要がある動作の規則のステートメント)で暗黙的な動作を指定するためのものです。
  • 契約(ルール)と実装(プログラムのルールのコーディング)には区別があります。
  • インターフェイスで指定されたメソッドは常に実装する必要があります(ある時点で)

それで、Java 8の新しいニュアンスは何ですか? "Optional Methods"といえば、次のいずれかが適切です。

1。実装が契約上オプションであるメソッド

「3番目のステートメント」は、抽象インターフェースメソッドを常に実装する必要があり、これがJava 8+でも当てはまることを示しています。ただし、Java Collections Frameworkのように、一部の抽象インターフェースメソッドをコントラクトで「オプション」として記述することは可能です。

この場合、インターフェイスを実装する作成者は、メソッドを実装しないことを選択できます。ただし、コンパイラは実装を要求するため、作成者は特定の実装クラスで必要のないオプションのメソッドにこのコードを使用します。

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

Java 7以前では、これは実際に唯一の種類の「オプションのメソッド」でした。つまり、実装されていない場合はUnsupportedOperationExceptionをスローしたメソッドです。この動作は、インターフェイスコントラクト(たとえば、Java Collections Frameworkのオプションのインターフェイスメソッド)によって必ず指定されます。

2。再実装がオプションであるデフォルトのメソッド

Java 8では、defaultメソッドの概念が導入されました。これらは、その実装がインターフェース定義自体によって提供され、提供されるメソッドです。通常、他のインターフェイスメソッド(「プリミティブ」)を使用してメソッド本体を記述できる場合、およびthisが「このインターフェイスを実装したクラスのオブジェクト」を意味する場合にのみ、デフォルトメソッドを提供できます。

デフォルトのメソッドは、インターフェースの規約を満たしている必要があります(他のインターフェースメソッドの実装と同様)。したがって、実装クラスでインターフェイスメソッドの実装を指定するのは、作成者の裁量です(動作が目的に適している限り)。

この新しい環境では、Java Collections Framework couldに書き換えられました。

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

このように、「オプション」メソッドadd()には、実装クラスが独自の新しい動作を提供しない場合にUnsupportedOperationExceptionをスローするデフォルトの動作があります。リストの契約。作成者が新しい要素をList実装に追加できないクラスを書いている場合、add()の実装はオプションです。なぜなら、デフォルトの動作はまさに必要なものだからです。

この場合、メソッドはインターフェース自体に実装されているため、上記の「3番目のステートメント」は引き続き有効です。

Optional結果を返すメソッド

最後の新しい種類のオプションのメソッドは、単にOptionalを返すメソッドです。 Optionalクラスは、nullの結果を処理する、明らかにオブジェクト指向の明確な方法を提供します。

新しいJava St​​reams APIを使用してコーディングするときによく見られるような流なプログラミングスタイルでは、任意の時点でnull結果が発生すると、プログラムはNullPointerExceptionでクラッシュします。 Optionalクラスは、クライアントコードをクラッシュさせることなく流なスタイルを可能にする方法で、クライアントコードにnullの結果を返すメカニズムを提供します。

4
scottb

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

たわごと英語でごめんね。

4
Wonson

すべてのコレクション実装の祖先クラスであるgrepCodeで AbstractCollection.Java のコードを実行すると、オプションのメソッドの意味を理解するのに役立ちます。以下に、AbstractCollectionクラスのadd(e)メソッドのコードを示します。 collection interfaceに従ってadd(e)メソッドはオプションです

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

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

4
IsAs

まあ、このトピックは...ええ..に対処されていますが、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でのみ機能します。

4
Thorben Kuck

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

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

0
Trent Steele

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

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

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
green0range

OPの質問には答えませんが、Java 8の時点で、インターフェイスにデフォルトのメソッドを追加することに注意する価値があります 実際に実行可能です 。インターフェイスのメソッドシグネチャに配置されたdefaultキーワードは、メソッドをオーバーライドするオプションを持つクラスを生成しますが、必須ではありません。

0
Troy Stopera