1. ホーム
  2. java

[解決済み] Java 8のインターフェイスメソッドで "final "が使えないのはなぜですか?

2022-03-15 03:38:19

質問

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 は明らかに矛盾するキーワードですが、デフォルトのキーワード自体は は、厳密には必要なかったのでしょう。 という微妙な違いを反映させるために、この矛盾は意図的なものだと推測しています。 クラスメソッドとボディの組み合わせ。 (単なるメソッド)と "ボディを持つインターフェースメソッド" (デフォルトのメソッド) などの違いがあり、まだ理解できていません。

のようなモディファイアのサポートは、ある時点から開始されます。 staticfinal をインターフェイスメソッドに適用することは、まだ十分に検討されていませんでした。 ブライアン・ゲッツの引用 :

もうひとつは、クラス構築をどこまでサポートするかということです。 インターフェイスのツール、例えばfinalメソッド、privateメソッド、protected メソッド、スタティックメソッドなど。 答えは、「まだわからない」です。

2011年末の当時から、明らかに static メソッドが追加されました。明らかに、これは JDK ライブラリ自体に多くの価値を付加するもので、例えば Comparator.comparing() .

質問です。

理由は何ですか? final (また static final は、Java 8 のインターフェースに採用されなかったのですか?

解決方法は?

この質問は、ある程度、次のことに関連しています。 Java 8のインターフェイスメソッドで "synchronized "が使えない理由は何ですか?

デフォルトのメソッドについて理解する上で重要なことは、設計の第一目標が インターフェイスの進化 インターフェイスを(平凡な)形質にしてしまうことではありません。 この2つの間には重複する部分があり、私たちは前者の邪魔にならないように後者に配慮しようとしましたが、この観点から見たとき、これらの質問は最もよく理解されるでしょう。 (なお、クラスメソッド というのは、インターフェイスメソッドは多重継承が可能であるため、どのような意図があろうとも、インターフェイスメソッドとは異なるからです)。

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

なぜファイナル・デフォルト・メソッドではないのか」に対する単純すぎる答えは、そうすればボディが単にデフォルトの実装になるのではなく、唯一の実装になるからです。 これは少し単純すぎる答えですが、この質問がすでに疑問の方向に向かっていることを示す手がかりになります。

Final Interfaceメソッドが疑問視されるもう一つの理由は、実装者に無理難題を突きつけるからです。 例えば、次のようなものがあったとします。

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 であったとしても、スーパークラスがすでにそのメソッドを実装していれば、デフォルトは無視されるでしょう。 (このような継承の振る舞いは、デフォルトメソッドの設計の中心であるインターフェイスの進化を反映したものです。 すでに実装されているインターフェイスにデフォルトメソッド(または既存のインターフェイスメソッドに対するデフォルトの実装)を追加することが可能で、インターフェイスを実装する既存のクラスの振る舞いを変えることなく、デフォルトメソッドが追加される前にすでに機能していたクラスは、デフォルトメソッドが存在しても同じように動作することが保証されます)。