web-dev-qa-db-ja.com

Javaにおける「最終クラス」のポイントは何ですか?

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

私はプログラミングが初めてなので、 プログラマが実際に自分のプログラムでこれを使っているのではないか と思っています。もしそうであれば、いつそれを使うのでしょうか。

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

488
newbie

もし彼らがそうするなら、いつ彼らはそれを使うのか。

finalクラスは単純に を拡張できないクラスです

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

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

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

ある意味ではい。

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


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

465
aioobe

Javaでは、final修飾子を持つ項目は変更できません。

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

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

セキュリティ上の理由から、クラスの継承を防ぐをしたい場合、finalが重要なシナリオが1つあります。これはあなたがを実行しているコードが誰かによってオーバーライドされないことを確認することを可能にします

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

29
Shay Rojansky

最も良い例は

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

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

19
javadeveloper

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

ここでのOOの原則への違反はありません、finalは単にNice対称性を提供することです。

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

15
biziclop

関連読書: The Open-Closed Principle / Bob Martin著。

キークォート:

ソフトウェア実体(クラス、モジュール、機能など)は、拡張のために開かれるべきですが、修正のために閉じられるべきです。

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

15

キーワードfinalそのものは、何かが最終的なものであり、いかなる方法でも変更されることは想定されていないことを意味します。クラスがfinalとマークされている場合は、そのクラスを拡張またはサブクラス化することはできません。しかし、問題は、なぜクラスをfinalとマークするのですか? IMOにはさまざまな理由があります。

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

クラスfinalを指定することで効率が向上すると聞いたことがありますが、率直に言って、この議論を重視することはできませんでした。

もしJavaがオブジェクト指向で、あなたがクラスファイナルを宣言するなら、それはクラスがオブジェクトの特性を持つという考えを止めませんか?

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

ちなみに、私たちは 継承よりも合成を好むfinalというキーワードが実際にこの原則を実行するのに役立つはずです。

10
i_am_zero

ファイナルクラスの問題を解決するには:

クラスを決勝戦にするには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クラスは、暗黙の最終クラスがどのように問題を引き起こす可能性があるかを示す良い例です。

したがって、暗黙的にコンストラクタを非公開にしてクラスをファイナルにする場合は、それをファイナルにする必要があります。

5
mel3kings

final classは、新しいメソッドを追加したときにパブリックAPIを壊さないようにすることができます

Baseクラスのバージョン1で、次のようにします。

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は壊れません。

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

5
Ben Wu

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

明確にするために、クラスfinalをマークすることはそのフィールドをfinalとしてマークしないため、オブジェクトのプロパティを保護するのではなく実際のクラス構造を保護します。

5
Esko

Javaでは、最後のキーワードは以下の場合に使用します。

  1. 最終変数
  2. 最終的な方法
  3. ファイナルクラス

Javaでは、final変数は再割り当てできません、 finalクラス は拡張できず、finalメソッドはオーバーライドできません。

3

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

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

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

3
Rahul

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

3
James

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

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

3
JuanZe

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

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

1
jaamit

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

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

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

1
Kingz

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

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

1
lok

メソッドEmployeeを持つgreetクラスがあるとしましょう。 greetメソッドが呼び出されると、単にHello everyone!を出力します。これがgreetメソッドの期待される動作です

public class Employee {

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

それでは、GrumpyEmployeeEmployeeのサブクラスにして、以下に示すようにgreetメソッドをオーバーライドしましょう。

public class GrumpyEmployee extends Employee {

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

今度は下記のコードでsayHelloメソッドを見てください。それはパラメータとしてEmployeeインスタンスを取り、それがHello everyone!と言うことを望んでいるgreetメソッドを呼び出しますしかししかし我々が得るのは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 Classがfinalとして宣言されていない場合、生意気なプログラマが引き起こす可能性のある混乱の量を想像してください。

1
Andy

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

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

1
skhaapioloir

最終クラスはそれ以上拡張できません。クラスをJavaで継承可能にする必要がない場合は、この方法を使用できます。

オーバーライドされないようにクラス内の特定のメソッドを作成するだけの場合は、最後のキーワードをそれらの前に置くことができます。そこにクラスはまだ継承可能です。

0
G. Kith

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

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

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

0
Nikolas