Javaのfinalメソッド:それは何を約束しますか?


141

Javaクラスでは、メソッドをとして定義してfinal、このメソッドがオーバーライドされないようにすることができます。

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

それは明らかであり、偶発的な上書きやパフォーマンスから保護するのに役立つかもしれませんが、それは私の質問ではありません。

私の質問は次のとおりです。OOPの観点から、メソッドを定義することによりfinal、クラス設計者はこのメソッドが常に説明どおりまたは暗黙のうちに機能することを約束することを理解しました。しかし、メソッドが何をしているかが単にプロパティを提供するよりも複雑である場合、これは多くの場合、クラスの作成者の影響の外にある可能性があります

構文上の制約は私には明らかですが、OOPの意味にはどのような意味がありますか?finalこの意味で、ほとんどのクラス作成者は正しく使用していますか?

finalメソッドはどのような「契約」を約束しますか?

回答:


155

前述のように、finalJavaメソッドと共に使用して、メソッドがオーバーライド(オブジェクトスコープの場合)または非表示(静的の場合)できないことをマークします。これにより、元の開発者はサブクラスで変更できない機能を作成でき、それが提供するすべての保証になります。

これは、メソッドが非パブリックフィールド/メソッドなどの他のカスタマイズ可能なコンポーネントに依存している場合でも、最終的なメソッドの機能はカスタマイズ可能であることを意味します。これは(ポリモーフィズムを使用して)部分的なカスタマイズが可能であるため、優れています。

カスタマイズを禁止する理由はいくつかあります。

  • パフォーマンス -一部のコンパイラ、特に副作用のないものは、操作を分析して最適化できます。

  • カプセル化されたデータを取得します -構築時に属性が設定され、変更してはならない不変オブジェクトを見てください。または、それらの属性から導出された計算値。良い例がJava Stringクラスです。

  • 信頼性と契約 -オブジェクトは、(プリミティブで構成されintchardouble等)及び/又はその他のオブジェクト。これらのコンポーネントに適用できるすべての操作が適用可能である必要はなく、それらがより大きなオブジェクトで使用される場合は論理的でさえあります。これfinalを確実にするために、修飾子を持つメソッドを使用できます。Counterクラスは良い例です。


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

public final int count()メソッドがでない場合、final次のようなことができます。

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

またはこのようなもの:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count

27

最終的な方法はどのような「契約」を約束しますか?

別の方法で見てください。最終でないメソッドは、独自の実装でオーバーライドできることを暗黙的に保証し、クラスは引き続き期待どおりに動作します。クラスがメソッドの上書きをサポートしていることを保証できない場合は、それをfinalにする必要があります。


しかし、この見解は、「この最終メソッドは常に約束どおりに動作する」という私の最初の質問を意味するのではなく、最終メソッドの内部から非最終メソッドを呼び出さないようにすべきですか?呼び出されたメソッドがオーバーライドされた可能性があるため、最終的なメソッドの動作を保証できないからです。
towi 2016

8

まず、finalフィールドとメソッドだけでなく、非抽象クラスをマークできます。この方法では、クラス全体をサブクラス化することはできません。したがって、クラスの動作は修正されます。

マーキングメソッドfinalは、これらのメソッドがfinal以外のメソッドを呼び出している場合、サブクラスでの動作が同じであることを保証しないことに同意します。実際に動作を修正する必要がある場合は、慣例と注意深い設計によってこれを実現する必要があります。そして、javadocでこれに注意することを忘れないでください!(javaドキュメント)

最後になりましたが、finalキーワードはJavaメモリモデル(JMM)で非常に重要な役割を果たします。JMMは、finalフィールドの可視性を実現するために適切な同期を必要としないことを保証しています。例えば:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  

はい、最終クラスについて知っています-簡単なケース。JMMの最終フィールドについての良い点、同期は必要ありません...うーん:これは「ポインタ」だけを参照しますよね?それが参照するオブジェクトを非同期に変更することもできます(OKではなくString、ユーザー定義クラスで)。しかし、「ファイナルは動作を保証しない」とあなたが言ったことは、まさに私の主張です。同意します。ドキュメントとデザインが重要です。
towi 2011

@towiはfinal、などの複合オブジェクトに加えられた変更の可視性を保証しませんMap
ビクターソロキン

0

「最終」の使用について、またそれがソフトウェアの全体的な設計契約にどのように影響するかについて、あなたが何らかの主張をすることができるかどうかはわかりません。開発者がこのメソッドをオーバーライドして、その方法でその契約を無効にできないことは保証されています。他方で、最終的な方法は、その値はサブクラスで設定されているクラスまたはインスタンス変数に依存してもよいし、他のクラスのメソッドを呼び出すことができるされているオーバーライド。したがって、最終的なものはせいぜい非常に弱い保証です。


1
はい、それは私が意味したことです。正しい。私は「弱い保証」という言葉が好きです:-)そして、私は(ほとんど)C ++が好きconstです。同様にchar const * const = "Hello"またはchar const * const addName(char const * const name) const...
towi

0

いいえ、それはクラス作成者の影響の外ではありません。派生クラスでオーバーライドすることはできないため、基本クラスの作成者が意図したとおりに機能します。

http://download.oracle.com/javase/tutorial/java/IandI/final.html

注目に値するのは、コンストラクターから呼び出されたメソッドがであるべきだと示唆している部分finalです。


1
まあ、技術的には基本クラスの作者が書いたことを行います。
ジョーイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.