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

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

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

さて、defaultと はfinal明らかに矛盾したキーワードですが、デフォルトのキーワード自体は厳密には要求されなかっただろうしたがって、私はこの矛盾が意図的なものであり、「本体を持つクラス メソッド」 (単なるメソッド) と「本体を持つインターフェース メソッド」 (デフォルト メソッド) の微妙な違い、つまり私がまだ理解していない違いを反映しているものと想定しています。

ある時点では、インターフェースメソッドのstaticやなどの修飾子のサポートはfinalまだ十分に検討されていませんでした。ブライアン・ゲッツ氏の言葉を引用:

もう1つの部分は、最終メソッド、プライベートメソッド、保護されたメソッド、静的メソッドなどのインターフェイスのクラス構築ツールをどの程度サポートするかということです。答えは、まだわかりません。

2011年後半以降、staticインターフェース内のメソッドのサポートが追加されました。これにより、JDKライブラリ自体に多くの価値が加わりました。Comparator.comparing()

質問:

final(そしてstatic final)Java 8 インターフェースに導入されなかった理由は何ですか?

ベストアンサー1

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

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

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

「なぜ final のデフォルト メソッドではないのか」という質問に対する答えは単純すぎるかもしれませんが、その場合、本体は単にデフォルトの実装ではなく、唯一の実装になるからです。これは少し単純すぎる答えですが、質問がすでに疑わしい方向に向かっているという手がかりになります。

final インターフェース メソッドが疑わしいもう 1 つの理由は、実装者にとって解決不可能な問題を引き起こすことです。たとえば、次のような場合を考えます。

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

interface B { 
}

class C implements A, B { 
}

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

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

ここで、 を再コンパイルしようとするとC、コンパイラーは のどの動作を継承すればよいかわからないため をオーバーライドする必要があることを通知しますfoo()(同じ動作を保持したい場合はCに委譲することもできます)。しかし、がデフォルト を作成し、の作成者の制御下にない場合はどうなるでしょうか。 は修復不能なほど壊れています。 をオーバーライドせずにコンパイルすることはできませんが、で final だった場合はオーバーライドできません。A.super.foo()BfinalACCfoo()foo()B

これは単なる一例ですが、ポイントは、メソッドのファイナリティは、単に動作を提供し、多重継承できるインターフェースよりも、単一継承クラス (一般に状態と動作を結合するクラス) の世界でより意味のあるツールであるということです。「最終的な実装に他のどのようなインターフェースが混在する可能性があるか」を考えるのは非常に難しく、インターフェース メソッドを final にすると、おそらくこれらの問題が発生します (そして、インターフェースを作成した人ではなく、それを実装しようとするかわいそうなユーザーに打撃を与えるでしょう)。

これらを許可しないもう 1 つの理由は、それらがあなたが考えている意味とは異なるからです。デフォルトの実装は、クラス (またはそのスーパークラス) がメソッドの宣言 (具象または抽象) を提供しない場合にのみ考慮されます。デフォルトのメソッドが final であっても、スーパークラスがすでにメソッドを実装している場合、デフォルトは無視されますが、これはおそらく、デフォルトの作成者が final と宣言したときに期待していたことではないはずです。(この継承の動作は、デフォルト メソッドの設計の中心であるインターフェイスの進化を反映しています。インターフェイスを実装する既存のクラスの動作を変更せずに、すでに実装されている既存のインターフェイスにデフォルト メソッド (または既存のインターフェイス メソッドのデフォルト実装) を追加できるようにする必要があります。これにより、デフォルト メソッドが追加される前にすでに動作していたクラスは、デフォルト メソッドがあっても同じように動作することが保証されます。)

おすすめ記事