私はJavaについての本を読んでいます、そしてそれはあなたがクラス全体を次のように宣言することができると言っていますfinal
。私はこれを使用するところは何も考えることができません。
私はプログラミングが初めてなので、疑問に思いますプログラマが実際に自分のプログラムでこれを使用する場合。もしそうなら、私はそれをよりよく理解し、いつ使うべきかを知ることができるように、いつ使うのですか。
Javaがオブジェクト指向で、クラスを宣言している場合final
それは、クラスがオブジェクトの特性を持つという考えを止めるのではありませんか。
もしそうなら、私はそれをよりよく理解し、いつ使うべきかを知ることができるように、いつ使うのですか。
Afinal
クラスは単にそのクラスです拡張できません。
(これは、クラスのオブジェクトへのすべての参照が、以下のように宣言されているかのように動作することを意味するわけではありません。final
。)
クラスをfinalとして宣言すると便利な場合は、この質問の回答で説明しています。
Javaがオブジェクト指向で、クラスを宣言している場合
final
それは、クラスがオブジェクトの特性を持つという考えを止めるのではありませんか。
ある意味ではい。
クラスをfinalとしてマークすることで、コードのその部分に対する言語の強力で柔軟な機能を無効にします。しかし、いくつかのクラスはそうすべきではありませんできるサブクラス化を考慮に入れるように設計されているのではありません。このような場合、OOPが制限されていても、クラスをfinalとしてマークするのが理にかなっています。 (ただし、最終クラスは、他の非最終クラスを拡張することができます。)
Javaでは、final
修飾子は変更できません。
これには、最終クラス、最終変数、および最終メソッドが含まれます。
final
修飾子を変更することはできません!"はカテゴリが多すぎ、実際には完全に正しいわけではありません。 Grady Boochが述べているように、「オブジェクトには状態、動作、およびアイデンティティがあります」。参照がfinalとしてマークされた後は、オブジェクトのIDを変更することはできませんが、変更する機会はありません。状態その値に新しい値を代入することによってfinal
(提供されている、もちろん提供されている)フィールドOracle Java認定を取得することを計画している人(1Z0-808など)は、試験に関してこの点について疑問がある可能性があるため、この点に留意する必要があります。 。 - Igor Soudakevitch
あなたがしたいとき、決勝が重要である1つのシナリオ継承を防ぐセキュリティ上の理由から、クラスのこれにより、実行しているコードを確実に確認できます。上書きできません誰かによって。
もう1つのシナリオは最適化です。Javaコンパイラは、最終クラスからの関数呼び出しをインライン化することを覚えているようです。だから、あなたが呼ぶならa.x()
そしてaが宣言されているfinal
、我々はコンパイル時にコードが何であるかを知っていて、そして呼び出している関数にインライン化することができます。これが実際に行われたかどうかはわかりませんが、最終的には可能性があります。
最も良い例は
パブリックファイナルクラスString
これは不変クラスであり、拡張することはできません。 もちろん、クラスをfinalにして不変にするだけではありません。
クラス階層を(Javaの場合のように)ツリーとして想像すると、抽象クラスはブランチにしかなれず、最終クラスはリーフにしかなれないものです。これらのカテゴリのいずれにも該当しないクラスは、ブランチとリーフの両方になることができます。
ここでオブジェクト指向の原則に違反することはありません、決勝戦は単に素晴らしい対称性を提供することです。
実際には、オブジェクトを不変にしたい場合、またはAPIを作成している場合は、そのクラスが単に拡張を目的としていないことをAPIのユーザーに知らせるために、finalを使用します。
関連する読書:オープン - クローズド原理ボブマーティンによって。
キークォート:
ソフトウェア実体(クラス、モジュール、 機能等)は、 延長、しかし閉鎖 変形。
のfinal
keywordは、メソッドで使用されているかクラスで使用されているかにかかわらず、Javaでこれを強制する手段です。
final
クラスをオープンではなく拡張のためにクローズにしますか?それとも私も文字通りそれを取っていますか? - Goran Jovicfinal
実装コードを変更のためにクローズし、継承による拡張のためにオープンする場合は、クラス/メソッドの宣言では意味がありません。 - Rogério
キーワードfinal
それ自体は、何かが最終的なものであり、いかなる方法でも修正されるべきではないことを意味します。マークされている場合はクラスfinal
それからそれは拡張されることができないか、または下位分類することができません。しかし問題は、なぜクラスをマークするのかということです。final
? IMOにはさまざまな理由があります。
そのマーキングクラスを聞いたことがあるfinal
効率は向上しますが、率直に言って、私はこの議論を重視することはできませんでした。
もしJavaがオブジェクト指向であり、あなたがクラスfinalを宣言するなら、それはそうではありません。 クラスがオブジェクトの特性を持つという考えをやめませんか。
おそらくそうです、しかし時々それは意図された目的です。時には、このクラスの拡張機能を犠牲にして、セキュリティなどのより大きな利点を達成するためにそうすることもあります。しかし、必要であれば、最終クラスでも1つのクラスを拡張できます。
ちなみに私たちはする必要があります継承よりも合成を好むそしてfinal
キーワードは実際にこの原則を実行するのに役立ちます。
クラスがマークされている場合final
つまり、クラスの構造は外部からは変更できません。これが最も目に見えるのは、伝統的な多相継承をしているときです。class B extends A
うまくいかない。それは基本的にあなたのコードの一部を保護する方法です。(ある程度)。
わかりやすく、マーキングクラスfinal
そのフィールドには印を付けませんfinal
そのため、オブジェクトのプロパティではなく実際のクラス構造を保護します。
クラスを "final"にするときは注意してください。最終クラスの単体テストを作成したい場合は、Michael C. Feathersの著書「レガシーコードで効果的に作業する」で説明されている依存関係を解消するテクニック「Subclass and Override Method」を使用するために、この最終クラスをサブクラス化できません。 。この本の中で、フェザーズ氏は、「真面目に言うと、封印されたものと最終的なものは間違った頭の間違いであり、決してプログラミング言語に追加されるべきではないと信じるのは簡単である。私たちの手の届かないところにある図書館、私たちはただトラブルを求めているのです。」
最終クラスの問題を解決するには
クラスを決勝にするには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を壊すことを避けることができます。
あなたのバージョン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は壊れません。
はい、セキュリティ上またはスピード上の理由から、これが望ましい場合もあります。 C ++でもできます。そうではないかもしれませんそれプログラムには適用できますが、フレームワークにも適用できます。http://www.glenmccl.com/perfj_025.htm
最後のクラスは拡張できないクラスです。また、メソッドをfinalとして宣言して、サブクラスでオーバーライドできないことを示すこともできます。
APIまたはライブラリを作成し、基本動作を変更するために拡張されないようにする場合は、クラスがサブクラス化されないようにすることが特に役立ちます。
クラスを最終的なものとして保つことの1つの利点: -
文字列クラスは最終的に保持されるので、だれもそのメソッドをオーバーライドして機能を変更することはできません。例えば、誰もlength()メソッドの機能を変更することはできません。常に文字列の長さを返します。
このクラスの開発者は、このクラスの機能を変更する人がいないことを望んでいたので、それを最終的なものにしました。
最終クラスは拡張できません。したがって、クラスに特定の振る舞いをさせ、誰かにメソッドをオーバーライドさせたくない場合(おそらく効率が悪く、より悪意のあるコード)、クラス全体を最終的なメソッドまたは特定のメソッドとして宣言することはできません。かわった。
クラスを宣言してもクラスのインスタンス化が妨げられるわけではないので、クラスがオブジェクトの特性を持たなくなるわけではありません。クラスで宣言されている方法と同じ方法でメソッドに固執する必要があるということです。
FINALを "End of the line"として考える - その男はもう子孫を作ることはできない。ですから、このように見たとき、あなたが遭遇するであろう現実世界のシナリオがたくさんあります。それはあなたがクラスに「行末」マーカーにフラグを立てることを要求します。ドメイン駆動設計です。ドメインで特定のENTITY(クラス)がサブクラスを作成できないことを要求している場合は、それをFINALとしてマークします。
私はあなたが "finalとしてタグ付けされるべき"クラスを継承するのを妨げるものは何もないことに注意すべきです。しかし、それは一般的に "継承の悪用"として分類され、そしてあなたのクラスの基本クラスから何らかの機能を継承したいと思うことが多いので行われます。
最善のアプローチは、ドメインを調べて、それを設計の決定に反映させることです。
上記のように、誰もメソッドの機能を変更できないようにしたい場合は、finalとして宣言できます。
例:ダウンロード/アップロード用のアプリケーションサーバーファイルのパス、オフセットに基づいて文字列を分割する、そのようなメソッドをFinalとして宣言できるので、これらのメソッドの機能は変更されません。そして、そのようなfinalメソッドを別のクラスに入れたい場合は、そのクラスをFinalクラスとして定義してください。したがって、Finalクラスはすべてのfinalメソッドを持ちます。ここで、Finalメソッドはnon-finalクラスで宣言および定義することができます。
Android Looperクラスはこれの良い実例です。http://developer.android.com/reference/android/os/Looper.html
Looperクラスは、他のクラスによってオーバーライドされることを意図しない特定の機能を提供します。したがって、ここにサブクラスはありません。
まもなく、finalとして宣言されたクラス、変数、またはメソッドは変更できません。
もっと重要なのは私の意見です。
正直なところ、私はキーワードだと思いますfinal
それが存在するとユーザーが何かを定義することが可能になるので、これは間違いです。ではない
final
。 Javaのすべてはfinal
デフォルトではインタフェースメソッドを除いて(その定義により)すべてのインスタンスメソッドを許可することは不運な選択でした。virtual
デフォルトでは、c#)重要なのは、ユーザーに最初に考えさせ、メソッドを次のように明示的に恥ずかしく定義することですvirtual
代わりに - それは彼が自分自身にこのメソッドが他の人にそれをオーバーライドさせるべきであるかどうか、それがそれであるかどうかを尋ねるようになるでしょうreally
彼のコードを再設計する必要があるか、彼を説得します。
あなたが持っているとしましょう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
。
オブジェクト指向は継承についてではなく、カプセル化についてです。そして継承はカプセル化を破ります。
多くの場合、クラスの決勝戦を宣言するのは完璧です。色や金額などの「価値」を表すオブジェクトは、最終的なものになる可能性があります。彼らは自立しています。
あなたがライブラリを書いているなら、あなたがそれらを派生するように明示的にインデントしない限りあなたのクラスをfinalにしてください。そうでなければ、人々はあなたのクラスを派生させ、メソッドをオーバーライドしてあなたの仮定/不変条件を破るかもしれません。これはセキュリティにも影響する可能性があります。
Joshua Blochは“ Effective Java”で、継承のために明示的に設計すること、またはそれを禁止することを推奨し、継承のための設計はそれほど簡単ではないと彼は指摘しています。