Java 8インターフェイスメソッドで「最終」が許可されないのはなぜですか?


335

Java 8の最も便利な機能の1つは、defaultインターフェースの新しいメソッドです。それらが導入された理由は基本的に2つあります(他にもある場合があります)。

APIデザイナーの観点から、インターフェイスメソッドで他の修飾子を使用できるようにしたいと思っていましたfinal。これは、便利なメソッドを追加するときに役立ち、クラスの実装における「偶発的な」オーバーライドを防ぎます。

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

上記がSenderクラスの場合、すでに一般的な方法です。

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

さて、defaultfinal明らかにキーワードを矛盾しているが、デフォルトでは自分自身をキーワード厳格に求められているだろう、私は微妙な違いを反映するために、この矛盾が意図的であると仮定しているので、「身体を持つクラス・メソッド」(ちょうどメソッド)と「インターフェースを本文付きのメソッド」(デフォルトのメソッド)、つまり、まだ理解していない違い。

ある時点で、Brian Goetzを引用して、インターフェースメソッドstaticなどの修飾子のサポートfinalはまだ完全には検討されていませんでした。

もう1つの部分は、finalメソッド、privateメソッド、protectedメソッド、staticメソッドなど、インターフェイスでクラス構築ツールをどの程度サポートするかです。答えは、まだわかりません。

2011年後半のその時以来、明らかに、staticインターフェイスのメソッドのサポートが追加されました。明らかに、これはJDKライブラリ自体に、などの多くの価値を追加しましたComparator.comparing()

質問:

Java 8インターフェイスに到達しなかった理由final(およびstatic final)は何ですか?


10
ウェットブランケットになって申し訳ありませんが、タイトルに示された質問がSOの条件の範囲内で回答される唯一の方法は、ブライアンゲッツまたはJSRエキスパートグループからの引用によるものです。BGがパブリックディスカッションを要求したことを理解していますが、それはSOの条件に反するものです。責任はここで引き下げられているように私には思えます。議論を刺激して理論的根拠を考え出すのは、専門家グループの仕事であり、より広範なJavaコミュニティプロセスの仕事です。そうではありません。したがって、私は「主に意見に基づく」ものとして締めくくることに投票します。
ローン侯爵

2
誰もが知っているfinalように、メソッドがオーバーライドされないようにし、インターフェイスから継承されたメソッドをオーバーライドする必要があることを確認すると、最終的にすることが理にかなっている理由がわかりません。メソッドを1回オーバーライドした後でメソッドが最終であることを示すことを除いて、その場合、おそらくQASの問題がありますか?この権利を理解していない場合は、kmowさせてください。おもしろそう
ヴィンス・エミー

22
@EJP:「私がそれを行うことができれば、あなたも、そしてあなた自身の質問に答えることができるので、尋ねる必要はなかったでしょう。」それはこのフォーラムのほとんどすべての質問に当てはまるでしょう?私はいつでも自分で5時間トピックをGoogleにして、他の誰もが同じように難しい方法を学ばなければならないことを学ぶことができました。または、SOがGoogleで非常によく参照されているため、誰かが(12票とこれまでの8星を含む)将来の誰もが利益を得ることができるように、誰かがさらに良い答えを返すまで数分待ちます。あ、はい。この質問、SOのQ&Aフォームに完全に適合します。
Lukas Eder、2014年

5
@VinceEmigh " ...インターフェイスから継承されたメソッドをオーバーライドする方法を確認する... " Java 8ではそうではありません。Java8では、メソッドをインターフェイスに実装できます。つまり、実装時にメソッドを実装する必要はありませんクラス。ここでfinalは、実装クラスがインターフェースメソッドのデフォルト実装をオーバーライドしないようにするのに役立ちます。
awksp 2014年

28
@EJPあなたは決して知りません:ブライアン・ゲッツは返信するかもしれません!
アッシリア

回答:


419

この質問は、ある程度、Java 8インターフェースメソッドで「同期」が許可されない理由とは何ですか。

デフォルトのメソッドについて理解する重要なことは、主な設計目標は「インターフェースを(普通の)特性に変える」ことではなく、インターフェースの進化であるということです。2つはある程度重複していますが、前者の邪魔にならない後者に対応しようとしましたが、これらの質問をこの観​​点から見ると最もよく理解できます。(クラスメソッド、インターフェイスメソッドが複数継承できるという事実により、意図が何であれ、インターフェイスメソッドとは異なることに注意してください。)

デフォルトメソッドの基本的な考え方は次のとおりです。これは、デフォルト実装を備えたインターフェースメソッドであり、派生クラスはより具体的な実装を提供できます。また、デザインセンターはインターフェイスの進化であったため、ソース互換性およびバイナリ互換性のある方法で、デフォルトのメソッドを事後的にインターフェイス追加できることが重要な設計目標でした。

「なぜ最終的なデフォルトメソッドではないのか」に対する単純すぎる答えは、本体が単にデフォルトの実装ではなく、唯一の実装になるということです。これは少し単純な答えですが、問題がすでに疑わしい方向に向かっているという手がかりを与えてくれます。

最終的なインターフェイスメソッドが疑わしいもう1つの理由は、実装者に不可能な問題を引き起こすことです。たとえば、次のように仮定します。

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

ここでは、すべてが良好です。からC継承foo()Aます。ここで、デフォルトでメソッドBを持つように変更したfooとします。

interface B { 
    default void foo() { ... }
}

さて、リコンパイルCに行くと、コンパイラーはどの振る舞いを継承するのかわからないfoo()ので、それCをオーバーライドする必要があります(そしてA.super.foo()同じ振る舞いを保持したい場合は委任することを選択できます)。Bはデフォルトを設定finalしておりA、作者の管理下にありませんCか?現在Cは回復不能に壊れています。オーバーライドせずにコンパイルするfoo()ことはできませんがfoo()、がfinalである場合はオーバーライドできませんB

これはほんの一例ですが、要点は、メソッドのファイナリティは実際には、単一の継承クラス(通常は状態と動作を結び付ける)の世界で、単に動作を提供して乗算できるインターフェイスよりも意味のあるツールであることです遺伝性の。「他のどのインターフェイスが最終的なインプリメンターに混在する可能性があるか」について推論するのはあまりにも難しく、インターフェイスメソッドをfinalにすることでこれらの問題が発生する可能性があります(そして、インターフェイスを作成した人ではなく、それを実装しようとする貧しいユーザー。)

それらを許可しない別の理由は、それらがあなたが彼らが意味すると思うことを意味しないということです。デフォルトの実装は、クラス(またはそのスーパークラス)がメソッドの宣言(コンクリートまたは抽象)を提供しない場合にのみ考慮されます。デフォルトのメソッドがfinalであるが、スーパークラスがすでにメソッドを実装している場合、デフォルトは無視されます。これは、デフォルトの作成者がfinalを宣言するときに期待していたものとはおそらく異なります。(この継承動作は、デフォルトメソッドのデザインセンターを反映したものです-インターフェイスの進化。デフォルトのメソッド(または既存のインターフェイスメソッドへのデフォルトの実装)を、既に実装されている既存のインターフェイスに、変更せずに追加できるはずです。インターフェースを実装する既存のクラスの動作


88
新しい言語機能に関する質問に答えてくれるのは素晴らしいことです。新しい機能の使用方法を理解する際に、設計の意図と詳細を明確にすることは非常に役立ちます。設計に関与した他の人々がSOに貢献していますか、それとも自分でやっていますか?私はjava-8タグの下であなたの回答をフォローします-他にも同じことをしている人がいるかどうか知りたいので、私もフォローすることができます。
Shorn、2014年

10
@Shorn Stuart Marksは、java-8タグでアクティブになっています。Jeremy Mansonは過去に投稿しました。Joshua Blochからのメッセージを見たことも覚えていますが、今は見つかりません。
アッシリア

1
デフォルトのインターフェースメソッドを思いついたのは、C#が、あまり考えられておらず、醜く実装された拡張メソッドを使って行うよりエレガントな方法であるからです。この質問に関しては、名前の衝突の種類を解決することは不可能であるという答えは問題を解決しますが、提供された残りの理由は説得力のない哲学でした。(インターフェイスメソッドをfinalにしたい場合は、自分とは異なる実装の提供を誰にも許可しないようにするために、自分自身のかなりいまいましい理由があるはずだと考えるべきです。)
Mike Nakis

1
それでも、名前の競合解決は、実装されている特定のインターフェイスの名前を実装メソッドの宣言に追加することにより、C#での方法と同様の方法で実現できたはずです。したがって、これらすべてから私が持ち帰るのは、Javaでデフォルトのインターフェースメソッドをfinalにすることはできないということです。これは、構文に追加の変更が必要になるためです。(あまりにものC-sharpish多分?)
マイク・Nakis

18
@Trying Abstractクラスは、状態を導入する、またはコアObjectメソッドを実装する唯一の方法です。デフォルトのメソッドは純粋な動作です。抽象クラスは、動作と状態を組み合わせたものです。
Brian Goetz、2015

43

lambdaメーリングリストでは、それについてたくさんの議論があります。それらすべてについて多くの議論が含まれていると思われるものの1つは次のとおりです。さまざまなインターフェイスメソッドの可視性(最終的な防御側でした)

この議論では、元の質問の作者であるタルデンがあなたの質問と非常によく似た質問をします。

すべてのインターフェースメンバーを公開するという決定は、実際に不幸な決定でした。内部設計でインターフェイスを使用すると、実装のプライベートな詳細が明らかになるのは大きな問題です。

言語に曖昧なまたは互換性を損なうニュアンスを追加せずに修正するのは難しいものです。その規模の互換性の破れと潜在的な微妙さは良心的ではないと思われるため、既存のコードを破らないソリューションが存在する必要があります。

「package」キーワードをアクセス指定子として再実行できる可能性があります。インターフェースに指定子がないことはパブリックアクセスを意味し、クラスに指定子がないことはパッケージアクセスを意味します。インターフェースでどの指定子が意味をなすかは不明確です-特に、開発者の知識の負担を最小限に抑えるために、アクセス指定子が存在する場合、クラスとインターフェースの両方で同じであることを確認する必要があります。

デフォルトのメソッドがない場合、インターフェースのメンバーの指定子は、少なくともインターフェース自体と同じくらいに見える必要があると推測しました(そのため、インターフェースは実際にはすべての表示可能なコンテキストで実装できます)。とても確かです。

これが範囲内での議論の可能性さえあるかどうかについて明確なコミュニケーションはありましたか?そうでない場合は、他の場所で開催する必要があります。

最終的にブライアンゲッツの答えは次のとおりです。

はい、これはすでに検討されています。

ただし、現実的な期待を設定しましょう。言語やVMの機能は、このような些細なものであっても、長いリードタイムを持っています。Java SE 8の新しい言語機能のアイデアを提案する時期はかなり過ぎました。

したがって、スコープの一部ではなかったため、実装されなかった可能性が高いです。検討するために時間内に提案されたことはありません。

この件に関する最終的な防御方法についての別の白熱した議論で、ブライアンは再び言った

そして、あなたはまさにあなたが望んだものを得ました。これがまさにこの機能が追加するものです-動作の多重継承。もちろん、私たちは人々がそれらを特性として使用することを理解しています。そして私たちは、彼らが提供する継承のモデルがシンプルでクリーンであることを保証するために努力してきました。同時に、私たちはそれらを単純かつクリーンに機能するものの境界を超えて押し出さないように選択しました。これは、「ああ、あなたは十分に行きませんでした」という反応につながる場合があります。しかし、実際には、このスレッドのほとんどは、グラスが98%しか満たされていないことに不満を抱いているようです。私はその98%を取り、それを使います!

したがって、これは、それが単にそれらの範囲の一部または設計の一部ではなかったという私の理論を補強します。彼らがしたことは、APIの進化の問題に対処するのに十分な機能を提供することでした。


4
今朝のグーグルオデッセイには、古い名前の「ディフェンダーメソッド」を含めるべきだったようです。これを掘り下げるための+1。
Marco13 2014年

1
歴史的事実を掘り起こす素敵なもの。あなたの結論は公式の回答
Lukas Eder

それが後方互換性を損なう理由がわかりません。以前はファイナルは許可されていませんでした。彼らは今ではプライベートを許可していますが、保護されていません。myeh ...プライベート缶実装していない...他のインターフェイスを拡張するインタフェースは、親インタフェースの部品を実装するかもしれないが、まだそれは他の人に過負荷に機能を公開するために持っている...
うーん

17

@EJPからのコメントで言及されている回答については、「THE」の回答を見つけて特定することは困難です。世界には、明確な回答提供できる人がおよそ2人(+/- 2人)います。そして疑いの余地はありませんが、答えは「最終的なデフォルトメソッドをサポートすることは、内部の呼び出し解決メカニズムを再構築する努力に値するものではなかった」のようなものにすぎないかもしれません。もちろんこれは憶測ですが、少なくともOpenJDKメーリングリストの(2人のうちの1人による)この声明のような微妙な証拠によって裏付けられています

「「デフォルトのデフォルト」メソッドが許可されたとしたら、内部のinvokespecialからユーザーに見えるinvokeinterfaceに書き換える必要があるかもしれません。」

そして、OpenJDKのMethod :: is_final_methodメソッドで現在実装されているように、メソッドがメソッドである場合、メソッドが(実際に)最終的なメソッドであるは単純に見なされないというような些細な事実。default

さらに、本当に「信頼できる」情報は、過度のウェブ検索やコミットログの読み取りによってさえ、実際に見つけることは困難です。invokeinterface命令に対応するインターフェイスメソッド呼び出しとクラスメソッド呼び出しの解決中に、invokevirtual命令に対応する可能性のあるあいまいさに関連している可能性があると思いました:invokevirtual命令の場合、メソッドを継承する必要があるため、単純なvtableルックアップがある場合がありますスーパークラスから、またはクラスによって直接実装されます。それとは対照的に、invokeinterface呼び出しはそれぞれの呼び出しサイトを調べてこの呼び出しが実際に参照しているインターフェースを見つける必要があります(これについては、HotSpot WikiのInterfaceCallsページで詳しく説明しています)。しかしながら、final方法は、どちらかに挿入されませんvtableのすべてで、またはに既存のエントリを置き換えるのvtable(参照klassVtable.cppを。ライン333)、同様に、デフォルトの方法は、中に既存のエントリを置き換えるのvtable(参照klassVtable.cpp、ライン202) 。したがって、実際の理由(したがって、答え)は(かなり複雑な)メソッド呼び出し解決メカニズムの奥深くに隠されている必要がありますが、これらの参照は、実際の答えを導き出すことができる他の人にとってのみ、それにもかかわらず役立つと見なされる可能性がありますそれから。


興味深い洞察をありがとう。ジョン・ローズの作品は興味深い痕跡です。ただし、@ EJPにはまだ同意しません。反例として、Peter Lawreyによる非常に興味深い非常に類似したスタイルの質問に対する私の回答を確認してください。歴史的事実を掘り出すこと可能であり、私はいつもここでスタックオーバーフロー(他の場所?)でそれらを見つけてうれしいです。もちろん、あなたの答えはまだ推測的なものであり、JLSの実装の詳細がJLSが何らかの方法で書き留められる最終的な理由(しゃれた)であると100%確信しているわけではありません...
Lukas Eder

@LukasEder確かに、これらの種類の質問興味深く、私見はQ&Aパターンに適合します。ここで論争を引き起こした2つの重要な点があると思います。1つ目は、「理由」を尋ねたことです。これは、多くの場合、正式に文書化されていない場合があります。たとえば、JLSに「理由」が記載されておらず、なぜ署名されていないints がないのかについては、stackoverflow.com
questions / 430346を

... ... 2番目は、「権威ある引用」のみを要求したことです。これにより、あえて答えを書く人の数が「数十」から「ほぼゼロ」に減少します。それとは別に、JVMの開発とJLSの記述がどのように織り込まれているのか、つまり、開発がJLSに書き込まれる内容にどの程度影響を与えるのかはわかりませんが、ここでは推測を避けます。 -)
Marco13、2014年

1
私はまだ私のケースを休ませる。私の他の質問の回答者を確認してください:-)これで、信頼できる回答により、永久に明らかになります。ここではStack Overflowでメソッドをサポートsynchronizedしないことに決定した理由を説明defaultます。
Lukas Eder 2014

3
@LukasEderなるほど、ここも同じです。誰がそれを期待できただろうか?その理由は、特にこのfinal質問に関してはかなり説得力があり、他の誰も同様の例を考えていないように見えた(または、おそらく、これらの例について考えていたが、回答するのに十分な権威を感じなかった)ことは、いくぶん控えめです。したがって、(申し訳ありませんが、これを行う必要があります:) 最後の言葉が話されています。
Marco13、2014

4

finalコンビニエンスインターフェースメソッドで指定する必要はないと思いますが、役立つかもしれないとは思いますが、コストがメリットを上回っているようです。

どちらの方法でも、デフォルトのメソッドに適切なjavadocを記述して、メソッドが何であり、許可されていないかを正確に示すことになります。このようにして、保証はありませんが、インターフェースを実装するクラスは実装を変更することが「許可されていません」。

誰でもCollection、インターフェイスに忠実なを作成し、メソッドで絶対に直感に反することを行うことができます。大規模な単体テストを作成する以外に、それを防ぐ方法はありません。


2
Javadocコントラクトは、質問に挙げた具体的な例の有効な回避策ですが、問題は、便利なインターフェイスメソッドのユースケースに関するものではありません。問題はfinal、Java 8 interfaceメソッドで許可されていないと決定された信頼できる理由についてです。不十分なコスト/利益比は良い候補ですが、これまでのところ、それは推測です。
Lukas Eder

0

defaultメソッドをinterface拡張するクラスが実装のinterface有無にかかわらずoverride、メソッド内にキーワードを追加します。しかし、実装クラスにオーバーライドさせたくないメソッドを追加したい場合はどうでしょうか?さて、2つのオプションを利用できました。

  1. default finalメソッドを追加します。
  2. staticメソッドを追加します。

現在、Javaは、メソッド名とシグネチャがまったく同じ、つまり重複しているメソッドがclass2つ以上実装されている場合、クラスにそのメソッドの実装を提供する必要があると述べています。メソッドの場合、実装を提供できず、行き詰まっています。そして、それがキーワードがインターフェースで使用されない理由です。interfacesdefaultdefault finalfinal

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