452

私はJavaについての本を読んでいます、そしてそれはあなたがクラス全体を次のように宣言することができると言っていますfinal。私はこれを使用するところは何も考えることができません。

私はプログラミングが初めてなので、疑問に思いますプログラマが実際に自分のプログラムでこれを使用する場合。もしそうなら、私はそれをよりよく理解し、いつ使うべきかを知ることができるように、いつ使うのですか。

Javaがオブジェクト指向で、クラスを宣言している場合finalそれは、クラスがオブジェクトの特性を持つという考えを止めるのではありませんか。

21 답변


437

もしそうなら、私はそれをよりよく理解し、いつ使うべきかを知ることができるように、いつ使うのですか。

Afinalクラスは単にそのクラスです拡張できません

(これは、クラスのオブジェクトへのすべての参照が、以下のように宣言されているかのように動作することを意味するわけではありません。final。)

クラスをfinalとして宣言すると便利な場合は、この質問の回答で説明しています。

Javaがオブジェクト指向で、クラスを宣言している場合finalそれは、クラスがオブジェクトの特性を持つという考えを止めるのではありませんか。

ある意味ではい。

クラスをfinalとしてマークすることで、コードのその部分に対する言語の強力で柔軟な機能を無効にします。しかし、いくつかのクラスはそうすべきではありませんできるサブクラス化を考慮に入れるように設計されているのではありません。このような場合、OOPが制限されていても、クラスをfinalとしてマークするのが理にかなっています。 (ただし、最終クラスは、他の非最終クラスを拡張することができます。)


関連記事Java:いつ最終クラスを作成するか


  • その答えに加えて、Effective Javaの原則の1つは、継承よりも構成を優先することです。 finalキーワードを使用すると、その原則を強制するのにも役立ちます。 - Riggy
  • "効率とセキュリティの面から主に行っています"私はこの発言をかなり頻繁に耳にしますが(ウィキペディアでさえこれを述べています)、私はまだこの議論の背後にある推論を理解していません。誰かが、例えば、最終的でないjava.lang.Stringがどのように非効率的または不安定な結果になったのかを説明することを気にかけていますか? - MRA
  • @MRA Stringをパラメータとして受け取るメソッドを作成する場合、Stringは不変であるため、そのメソッドは不変であると想定します。この結果として、私はStringオブジェクトのどのメソッドも安全に呼び出すことができ、渡されたStringを変更することはできません。 Stringを拡張し、substringの実装を変更して実際のStringを変更すると、不変であると予想されていたStringオブジェクトは不変にはなりません。 - Cruncher
  • @Sortofabeginnerそして、すべてのStringメソッドとフィールドをfinalにしたいと言ったらすぐに、追加機能を持つクラスを作成できるようになります。その時点では、ストリングを持つクラスを作成するだけでよいでしょう。その文字列を操作するメソッドを作成します。 - Cruncher
  • @Shay finalは(とりわけ)オブジェクトを不変にするために使われるので、私はそれらがお互いに何の関係もないとは言いません。こちらをご覧くださいdocs.oracle.com/javase/tutorial/essential/concurrency/… - Celeritas

161

Javaでは、final修飾子は変更できません。

これには、最終クラス、最終変数、および最終メソッドが含まれます。

  • 最後のクラスは他のクラスによって拡張することはできません
  • 最後の変数は別の値に再割り当てできません
  • 最後のメソッドは上書きできません


  • 実際の質問はなぜではない。 - Francesco Menzani
  • 「Javaでは、final修飾子を変更することはできません!"はカテゴリが多すぎ、実際には完全に正しいわけではありません。 Grady Boochが述べているように、「オブジェクトには状態、動作、およびアイデンティティがあります」。参照がfinalとしてマークされた後は、オブジェクトのIDを変更することはできませんが、変更する機会はありません。状態その値に新しい値を代入することによってfinal(提供されている、もちろん提供されている)フィールドOracle Java認定を取得することを計画している人(1Z0-808など)は、試験に関してこの点について疑問がある可能性があるため、この点に留意する必要があります。 。 - Igor Soudakevitch

27

あなたがしたいとき、決勝が重要である1つのシナリオ継承を防ぐセキュリティ上の理由から、クラスのこれにより、実行しているコードを確実に確認できます。上書きできません誰かによって。

もう1つのシナリオは最適化です。Javaコンパイラは、最終クラスからの関数呼び出しをインライン化することを覚えているようです。だから、あなたが呼ぶならa.x()そしてaが宣言されているfinal、我々はコンパイル時にコードが何であるかを知っていて、そして呼び出している関数にインライン化することができます。これが実際に行われたかどうかはわかりませんが、最終的には可能性があります。


  • インライン化は通常、実行時にJust-In-Timeコンパイラによってのみ行われます。それもfinalなしで動作しますが、JITコンパイラは、拡張クラスが存在しないこと(またはこれらの拡張クラスがこのメソッドに触れないこと)を確実にするためにもう少し作業が必要です。 - Paŭlo Ebermann
  • インライン展開と最適化の問題についての良い記事がここにあります。lemire.me/blog/archives/2014/12/17/… - Josh Hemann

19

最も良い例は

パブリックファイナルクラスString

これは不変クラスであり、拡張することはできません。 もちろん、クラスをfinalにして不変にするだけではありません。


  • Hehe、時々それは彼ら自身からRube Goldergian開発者を保護します。 - Zoidberg

15

クラス階層を(Javaの場合のように)ツリーとして想像すると、抽象クラスはブランチにしかなれず、最終クラスはリーフにしかなれないものです。これらのカテゴリのいずれにも該当しないクラスは、ブランチとリーフの両方になることができます。

ここでオブジェクト指向の原則に違反することはありません、決勝戦は単に素晴らしい対称性を提供することです。

実際には、オブジェクトを不変にしたい場合、またはAPIを作成している場合は、そのクラスが単に拡張を目的としていないことをAPIのユーザーに知らせるために、finalを使用します。


15

関連する読書:オープン - クローズド原理ボブマーティンによって。

キークォート:

ソフトウェア実体(クラス、モジュール、   機能等)は、   延長、しかし閉鎖   変形。

finalkeywordは、メソッドで使用されているかクラスで使用されているかにかかわらず、Javaでこれを強制する手段です。


  • @ Sean:宣言していませんfinalクラスをオープンではなく拡張のためにクローズにしますか?それとも私も文字通りそれを取っていますか? - Goran Jovic
  • グローバルに@Goranを適用する、はい。重要なのは、変更を加えたくない場所にfinalを選択的に適用することです(もちろん拡張のための良いフックを提供するために)。 - Sean Patrick Floyd
  • @ Sean:ああ、間違いなく。 - Goran Jovic
  • OCPでは、「修正」は「拡張」とは、ソースコードを修正することを指す。実装の継承を指します。したがって、の使用final実装コードを変更のためにクローズし、継承による拡張のためにオープンする場合は、クラス/メソッドの宣言では意味がありません。 - Rogério
  • @Rogerio私はからの参照(および解釈)を借用しましたSpring Framework Reference(MVC)。私見これは元のバージョンよりもはるかに理にかなっています。 - Sean Patrick Floyd

9

キーワードfinalそれ自体は、何かが最終的なものであり、いかなる方法でも修正されるべきではないことを意味します。マークされている場合はクラスfinalそれからそれは拡張されることができないか、または下位分類することができません。しかし問題は、なぜクラスをマークするのかということです。final? IMOにはさまざまな理由があります。

  1. 標準化:いくつかのクラスは標準的な機能を実行し、それらは修正されることを意図されていない。文字列操作や数学関数などに関連するさまざまな機能を実行するクラス
  2. セキュリティ上の理由:認証やパスワードに関連したさまざまな機能を実行するクラスを作成することがありますが、それらが他人に変更されたくない場合があります。

そのマーキングクラスを聞いたことがあるfinal効率は向上しますが、率直に言って、私はこの議論を重視することはできませんでした。

もしJavaがオブジェクト指向であり、あなたがクラスfinalを宣言するなら、それはそうではありません。   クラスがオブジェクトの特性を持つという考えをやめませんか。

おそらくそうです、しかし時々それは意図された目的です。時には、このクラスの拡張機能を犠牲にして、セキュリティなどのより大きな利点を達成するためにそうすることもあります。しかし、必要であれば、最終クラスでも1つのクラスを拡張できます。

ちなみに私たちはする必要があります継承よりも合成を好むそしてfinalキーワードは実際にこの原則を実行するのに役立ちます。


5

クラスがマークされている場合finalつまり、クラスの構造は外部からは変更できません。これが最も目に見えるのは、伝統的な多相継承をしているときです。class B extends Aうまくいかない。それは基本的にあなたのコードの一部を保護する方法です。(ある程度)

わかりやすく、マーキングクラスfinalそのフィールドには印を付けませんfinalそのため、オブジェクトのプロパティではなく実際のクラス構造を保護します。


  • オブジェクトプロパティとはどういう意味ですか?クラスがfinalと宣言されていれば、クラスのメンバ変数を変更できるという意味ですか?だから最後のクラスの唯一の目的は継承を防ぐことです。 - Adam Lyu

5

クラスを "final"にするときは注意してください。最終クラスの単体テストを作成したい場合は、Michael C. Feathersの著書「レガシーコードで効果的に作業する」で説明されている依存関係を解消するテクニック「Subclass and Override Method」を使用するために、この最終クラスをサブクラス化できません。 。この本の中で、フェザーズ氏は、「真面目に言うと、封印されたものと最終的なものは間違った頭の間違いであり、決してプログラミング言語に追加されるべきではないと信じるのは簡単である。私たちの手の届かないところにある図書館、私たちはただトラブルを求めているのです。」


5

最終クラスの問題を解決するには

クラスを決勝にするには2つの方法があります。 1つは、クラス宣言でキーワードfinalを使用することです。

public final class SomeClass {
  //  . . . Class contents
}

クラスをfinalにする2つ目の方法は、そのすべてのコンストラクタをprivateとして宣言することです。

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

このTestクラスの外観を示すために、それが実際のファイナルであることがわかった場合は、それをファイナルとマークすることで手間が省けます。一目見ただけでパブリックに見えます。

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

残念ながら、クラスの唯一のコンストラクタはprivateなので、このクラスを拡張することは不可能です。 Testクラスの場合、クラスがfinalになるべきであるという理由はありません。 Testクラスは、暗黙の最終クラスがどのように問題を引き起こす可能性があるかの良い例です。

ですから、暗黙的にコンストラクタを非公開にしてクラスをファイナルにするときは、それをファイナルにするべきです。


4

final class新しいメソッドを追加するときにパブリックAPIを壊すことを避けることができます。

あなたのバージョン1でそれを仮定しなさいBaseあなたがするクラス:

public class Base {}

そしてクライアントは:

class Derived extends Base {
    public int method() { return 1; }
}

その後、バージョン2であなたが追加したい場合methodメソッドBase

class Base {
    public String method() { return null; }
}

それはクライアントコードを壊すでしょう。

使ったことがあればfinal class Base代わりに、クライアントは継承することができず、メソッドを追加してもAPIは壊れません。


3

はい、セキュリティ上またはスピード上の理由から、これが望ましい場合もあります。 C ++でもできます。そうではないかもしれませんそれプログラムには適用できますが、フレームワークにも適用できます。http://www.glenmccl.com/perfj_025.htm


3

最後のクラスは拡張できないクラスです。また、メソッドをfinalとして宣言して、サブクラスでオーバーライドできないことを示すこともできます。

APIまたはライブラリを作成し、基本動作を変更するために拡張されないようにする場合は、クラスがサブクラス化されないようにすることが特に役立ちます。


3

クラスを最終的なものとして保つことの1つの利点: -

文字列クラスは最終的に保持されるので、だれもそのメソッドをオーバーライドして機能を変更することはできません。例えば、誰もlength()メソッドの機能を変更することはできません。常に文字列の長さを返します。

このクラスの開発者は、このクラスの機能を変更する人がいないことを望んでいたので、それを最終的なものにしました。


1

最終クラスは拡張できません。したがって、クラスに特定の振る舞いをさせ、誰かにメソッドをオーバーライドさせたくない場合(おそらく効率が悪く、より悪意のあるコード)、クラス全体を最終的なメソッドまたは特定のメソッドとして宣言することはできません。かわった。

クラスを宣言してもクラスのインスタンス化が妨げられるわけではないので、クラスがオブジェクトの特性を持たなくなるわけではありません。クラスで宣言されている方法と同じ方法でメソッドに固執する必要があるということです。


  • これはどのようにすでに言われたことに追加しますか? - user1440897

1

FINALを "End of the line"として考える - その男はもう子孫を作ることはできない。ですから、このように見たとき、あなたが遭遇するであろう現実世界のシナリオがたくさんあります。それはあなたがクラスに「行末」マーカーにフラグを立てることを要求します。ドメイン駆動設計です。ドメインで特定のENTITY(クラス)がサブクラスを作成できないことを要求している場合は、それをFINALとしてマークします。

私はあなたが "finalとしてタグ付けされるべき"クラスを継承するのを妨げるものは何もないことに注意すべきです。しかし、それは一般的に "継承の悪用"として分類され、そしてあなたのクラスの基本クラスから何らかの機能を継承したいと思うことが多いので行われます。

最善のアプローチは、ドメインを調べて、それを設計の決定に反映させることです。


1

上記のように、誰もメソッドの機能を変更できないようにしたい場合は、finalとして宣言できます。

例:ダウンロード/アップロード用のアプリケーションサーバーファイルのパス、オフセットに基づいて文字列を分割する、そのようなメソッドをFinalとして宣言できるので、これらのメソッドの機能は変更されません。そして、そのようなfinalメソッドを別のクラスに入れたい場合は、そのクラスをFinalクラスとして定義してください。したがって、Finalクラスはすべてのfinalメソッドを持ちます。ここで、Finalメソッドはnon-finalクラスで宣言および定義することができます。


1

Android Looperクラスはこれの良い実例です。http://developer.android.com/reference/android/os/Looper.html

Looperクラスは、他のクラスによってオーバーライドされることを意図しない特定の機能を提供します。したがって、ここにサブクラスはありません。


0

まもなく、finalとして宣言されたクラス、変数、またはメソッドは変更できません。

もっと重要なのは私の意見です。

正直なところ、私はキーワードだと思いますfinalそれが存在するとユーザーが何かを定義することが可能になるので、これは間違いです。ではない final。 Javaのすべてはfinalデフォルトではインタフェースメソッドを除いて(その定義により)すべてのインスタンスメソッドを許可することは不運な選択でした。virtualデフォルトでは、)重要なのは、ユーザーに最初に考えさせ、メソッドを次のように明示的に恥ずかしく定義することですvirtual代わりに - それは彼が自分自身にこのメソッドが他の人にそれをオーバーライドさせるべきであるかどうか、それがそれであるかどうかを尋ねるようになるでしょうreally彼のコードを再設計する必要があるか、彼を説得します。


0

あなたが持っているとしましょうEmployeeメソッドを持つクラスgreet。ときにgreetメソッドは単純に出力されると呼ばれますHello everyone!。だからそれは予想される行動greet方法

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

それでは、GrumpyEmployeeサブクラスEmployeeそして上書きgreet以下に示すような方法。

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

今、以下のコードで見ているsayHello方法。それはとりますEmployeeパラメータとしてインスタンスを作成し、それが言うことを期待してgreetメソッドを呼び出しますHello everyone!しかし私たちが得るのはGet lost!。この動作の変更は、Employee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

この状況は避けたもしEmployeeクラスができましたfinal。生意気なプログラマが次のような場合に引き起こす混乱の量を想像してください。Stringクラスがとして宣言されていませんfinal


-2

オブジェクト指向は継承についてではなく、カプセル化についてです。そして継承はカプセル化を破ります。

多くの場合、クラスの決勝戦を宣言するのは完璧です。色や金額などの「価値」を表すオブジェクトは、最終的なものになる可能性があります。彼らは自立しています。

あなたがライブラリを書いているなら、あなたがそれらを派生するように明示的にインデントしない限りあなたのクラスをfinalにしてください。そうでなければ、人々はあなたのクラスを派生させ、メソッドをオーバーライドしてあなたの仮定/不変条件を破るかもしれません。これはセキュリティにも影響する可能性があります。

Joshua Blochは“ Effective Java”で、継承のために明示的に設計すること、またはそれを禁止することを推奨し、継承のための設計はそれほど簡単ではないと彼は指摘しています。

リンクされた質問


関連する質問

最近の質問