私はJavaについての本を読んでいます、そしてそれはあなたがクラス全体をfinal
として宣言できると言っています。私はこれを使用するところは何も考えることができません。
私はプログラミングが初めてなので、 プログラマが実際に自分のプログラムでこれを使っているのではないか と思っています。もしそうであれば、いつそれを使うのでしょうか。
もしJavaがオブジェクト指向で、あなたがクラスfinal
を宣言するなら、それはクラスがオブジェクトの特性を持つという考えを止めませんか?
もし彼らがそうするなら、いつ彼らはそれを使うのか。
final
クラスは単純に を拡張できないクラスです 。
(これは、クラスのオブジェクトへのすべての参照が、それらがfinal
として宣言されているかのように動作することを意味するのではありません。)
クラスをfinalとして宣言すると便利な場合は、この質問の回答で説明します。
もしJavaがオブジェクト指向で、あなたがクラス
final
を宣言するなら、それはクラスがオブジェクトの特性を持つという考えを止めませんか?
ある意味ではい。
クラスをfinalとしてマークすることで、コードのその部分に対する言語の強力で柔軟な機能を無効にします。しかしながら、いくつかのクラスはサブクラス化を考慮に入れるように設計されるべきではありません(そしてある場合にはcan not)。このような場合、OOPが制限されていても、クラスをfinalとしてマークするのが理にかなっています。 (ただし、最後のクラスは別の最後ではないクラスを拡張することができます。)
関連記事: Java:いつ最終クラスを作るのか
Javaでは、final
修飾子を持つ項目は変更できません。
これには最終クラス、最終変数、および最終メソッドが含まれます。
セキュリティ上の理由から、クラスの継承を防ぐをしたい場合、finalが重要なシナリオが1つあります。これはあなたがを実行しているコードが誰かによってオーバーライドされないことを確認することを可能にします。
もう1つのシナリオは最適化のためのものです。Javaコンパイラは、最終クラスからの関数呼び出しをインライン化することを覚えているようです。したがって、a.x()
を呼び出し、aがfinal
と宣言されている場合、コンパイル時にコードがどのようになり、呼び出し元の関数にインライン展開できるかがわかります。これが実際に行われたかどうかはわかりませんが、最終的には可能性があります。
最も良い例は
パブリックファイナルクラスString
これは不変クラスであり、拡張することはできません。もちろん、クラスを最終的に不変にするだけではありません。
クラス階層を(Javaのように)ツリーとして想像すると、抽象クラスはブランチにしかなれず、最終クラスはリーフにしかなれないものです。これらのカテゴリのいずれにも該当しないクラスは、ブランチとリーフの両方になることができます。
ここでのOOの原則への違反はありません、finalは単にNice対称性を提供することです。
実際には、オブジェクトを不変にしたい場合、またはAPIを作成している場合は、そのクラスが単に拡張を目的としていないことをAPIのユーザーに知らせるためにfinalを使用します。
関連読書: The Open-Closed Principle / Bob Martin著。
キークォート:
ソフトウェア実体(クラス、モジュール、機能など)は、拡張のために開かれるべきですが、修正のために閉じられるべきです。
final
キーワードは、メソッドで使用されているかクラスで使用されているかにかかわらず、Javaでこれを強制する手段です。
キーワードfinal
そのものは、何かが最終的なものであり、いかなる方法でも変更されることは想定されていないことを意味します。クラスがfinal
とマークされている場合は、そのクラスを拡張またはサブクラス化することはできません。しかし、問題は、なぜクラスをfinal
とマークするのですか? IMOにはさまざまな理由があります。
クラスfinal
を指定することで効率が向上すると聞いたことがありますが、率直に言って、この議論を重視することはできませんでした。
もしJavaがオブジェクト指向で、あなたがクラスファイナルを宣言するなら、それはクラスがオブジェクトの特性を持つという考えを止めませんか?
おそらくそうです、しかし時々それは意図された目的です。時には、このクラスの拡張機能を犠牲にして、セキュリティなどのより大きな利点を達成するためにそれを行います。しかし、必要であれば、最終クラスでも1つのクラスを拡張できます。
ちなみに、私たちは 継承よりも合成を好む とfinal
というキーワードが実際にこの原則を実行するのに役立つはずです。
ファイナルクラスの問題を解決するには:
クラスを決勝戦にするには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クラスは、暗黙の最終クラスがどのように問題を引き起こす可能性があるかを示す良い例です。
したがって、暗黙的にコンストラクタを非公開にしてクラスをファイナルにする場合は、それをファイナルにする必要があります。
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」を使用するためにこの最終クラスをサブクラス化することはできません。 。この本の中で、フェザーズ氏は、次のように述べている。「真剣に、封印されたものと最終的なものは間違った考えの間違いであり、決してプログラミング言語に追加されるべきではなかったと信じるのは簡単です。しかし本当の欠点は私たちにあります。私たちの手の届かないところにある図書館、私たちはただトラブルを求めているのです。」
クラスがfinal
とマークされている場合、それはそのクラスの構造が外部のものによって変更できないことを意味します。これが最も目に見えるのは、伝統的な多相継承をしているときですが、基本的にclass B extends A
はうまくいきません。それは基本的にあなたのコードの一部を保護する方法です (ある程度まで) 。
明確にするために、クラスfinal
をマークすることはそのフィールドをfinal
としてマークしないため、オブジェクトのプロパティを保護するのではなく実際のクラス構造を保護します。
Javaでは、最後のキーワードは以下の場合に使用します。
Javaでは、final変数は再割り当てできません、 finalクラス は拡張できず、finalメソッドはオーバーライドできません。
クラスを最終的なものとして保つことの1つの利点: -
文字列クラスは最終的に保持されるので、誰もそのメソッドをオーバーライドして機能を変更することはできません。例えば、誰もlength()メソッドの機能を変更することはできません。常に文字列の長さを返します。
このクラスの開発者は、このクラスの機能を変更する人がいないことを望んでいたので、それを最終的なものにしました。
はい、セキュリティ上の理由またはスピード上の理由から、これが望ましい場合もあります。 C++でもできます。 that はプログラムには適用できませんが、フレームワークには適用できます。 http://www.glenmccl.com/perfj_025.htm
最後のクラスは拡張できないクラスです。また、メソッドをfinalとして宣言して、サブクラスでオーバーライドできないことを示すこともできます。
クラスをサブクラス化しないようにすることは、APIまたはライブラリを作成し、基本動作を変更するために拡張されないようにしたい場合に特に便利です。
Android Looperクラスはこの良い実例です。 http://developer.Android.com/reference/Android/os/Looper.html
Looperクラスは、他のクラスによってオーバーライドされることを意図していない特定の機能を提供します。したがって、ここではサブクラスはありません。
fINALを "End of the line"と考えてください - その男はもう子孫を生み出すことはできません。したがって、このように見た場合、遭遇することになる実世界のシナリオが多数あります。それには、クラスに「行末」マーカーを立てる必要があります。それはドメイン駆動設計です - あなたのドメインが与えられたENTITY(クラス)がサブクラスを作成できないことを要求するならば、それをFINALとしてマークしてください。
私はあなたが "finalとしてタグ付けされるべき"クラスを継承するのを妨げるものは何もないことに注意すべきです。しかし、それは一般的に "継承の悪用"として分類され、ほとんどの場合あなたはあなたのクラスの基底クラスから何らかの機能を継承したいと思うのでこれを行います。
最善の方法は、ドメインを調べて、それを設計の決定に反映させることです。
上記のように、誰もメソッドの機能を変更できないようにしたい場合は、それをfinalとして宣言できます。
例:ダウンロード/アップロード用のアプリケーションサーバーファイルのパス、オフセットに基づいて文字列を分割する、そのようなメソッドをFinalとして宣言できるので、これらのメソッドの機能は変更されません。そして、そのようなfinalメソッドを別のクラスに入れたい場合は、そのクラスをFinalクラスとして定義してください。したがって、Finalクラスはすべてのfinalメソッドを持ちます。ここで、Finalメソッドはnon-finalクラスで宣言および定義することができます。
メソッド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
インスタンスを取り、それが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
として宣言されていない場合、生意気なプログラマが引き起こす可能性のある混乱の量を想像してください。
最終クラスは拡張できません。したがって、クラスに特定の振る舞いをさせ、誰かにメソッドをオーバーライドさせたくない場合(おそらく効率が悪く、より悪意のあるコード)、クラス全体を最終的なメソッドまたは特定のメソッドとして宣言することはできません。かわった。
クラスを宣言してもクラスのインスタンス化が妨げられるわけではないので、クラスがオブジェクトの特性を持たなくなるわけではありません。クラスで宣言されているとおりにメソッドに固執する必要があるということです。
最終クラスはそれ以上拡張できません。クラスをJavaで継承可能にする必要がない場合は、この方法を使用できます。
オーバーライドされないようにクラス内の特定のメソッドを作成するだけの場合は、最後のキーワードをそれらの前に置くことができます。そこにクラスはまだ継承可能です。
まもなく、finalとして宣言されたクラス、変数、またはメソッドは変更できません。
もっと重要なのは私の意見です:
正直なところ、キーワードfinal
は、その存在によってユーザーがnotfinal
であるものを定義できるため、間違いであると考えています。 Javaのすべては、インターフェースメソッド(定義により)を除き、デフォルトでfinal
でなければなりません。すべてのインスタンスメソッドをデフォルトでvirtual
にすることは不幸な選択でした( c# に関して)。重要なのは、ユーザーが最初に考えて、メソッドをvirtual
として恥ずかしく明示的に定義することです-それは、このメソッドが他の人にそれをオーバーライドさせるべきかどうか、それがreally
の必要性か、コードを再設計するように説得するかを彼自身に尋ねさせるでしょう。