Javaで該当する場合は常に「最終」修飾子を使用する[終了]


194

Javaでは、すべての変数(ローカルまたはクラス)を宣言し、それらが実際の場合はfinalパラメータを宣言する習慣があります。

これにより、コードがより詳細になりますが、これにより、コードの読み取り/把握が容易になり、意図が明確に示されるため、間違いを防ぐことができます。

これについてあなたはどう思いますか、そしてあなたは何をしますか?


13
これは宗教的な議論につながる可能性があります。好きな人もいれば嫌いな人もいます。私は最終的なフィールドが好きですが、必要でない限り、最終的なローカル変数は好きではありません。どちらかの反対票がどちらになるかはわかりません。私はアレックスミラーに同意します。;)
Peter Lawrey

ファイナルでコードが雑然としているのが嫌いなのかどうかは理解できます。しかし、これは優れたエディタが解決できる問題です:bugs.eclipse.org/bugs/show_bug.cgi
id=

回答:


184

それはすべて、優れたコーディングスタイルに関係していると思います。もちろん、finalどこでも多くの修飾子を使用しなくても、優れた堅牢なプログラムを書くことができますが、考えてみると...

変更しfinalはならないすべてのものに追加すると、あなた(または次のプログラマー、コードに取り組んでいる人)がコードの原因となった思考プロセスを誤って解釈または誤用する可能性を狭めるだけです。少なくとも、以前は不変だったものを変更したいときに、いくつかのベルが鳴るはずです。

最初は、それは一種のたくさん見ることが厄介なルックスのfinalあなたのコード内のキーワードを、しかしかなりすぐには言葉自体に気付いて停止されますと、単純に考えるだろう、その-事-意志-決して変更-から-この-ポイント-上に(あなたは私からそれを取ることができます;-)

いい練習だと思います。私はいつもそれを使っているわけではありませんが、できて、何かfinalにラベルを付けるのが理にかなっているときは、それをやります。


16
パラメーターにfinalキーワードを使用する代わりに、FindBugsなどの静的分析ツールを使用して、パラメーター変数を再割り当てしないようにすることができます。このようにして、構文の負担を取り除き、継続的インテグレーションツールのFindBugsチェックを介してそれを実施できます。
TimoWestkämper10年

20
@Timoこれは機能しますが、開発者がコードで作業しているときではなく、チェックイン後に確認する必要があるという欠点があります。についてfinalは、コンパイラーは間違いを犯した場合、続行を許可しません。
Mike

9
Eclipseには、final保存するたびに、可能な限り変更しない変数を追加するオプションがあります。
Bert F

22
+1大好きfinalです。それは98%の時間で違いを生じさせません、それは時間の%1のマイナーな不便ですが、1%の時間は私が愚かまたは意図されていないことをすることから私を救う価値があります。
Bert F

14
@BertFええ、finalコードを「クリーンアップ...」するときはいつでもEclipseに修飾子を随所に追加させます。「どこでも」とは、catch()ブロック内であっても意味し、前述のように、しばらくするとそれらに気付かないでしょう。もちろん、言語を作成する場合final、がデフォルトで、varまたはmodifiableがオプションのキーワードになります。
Maarten Bodewes 2012年

191

にこだわる:

  • 最終フィールド-フィールドを最終としてマークすると、建設の終わりまでにフィールドが強制的に設定され、そのフィールド参照は不変になります。これにより、フィールドを安全に公開できるようになり、後で読み取るときに同期する必要がなくなります。(オブジェクト参照の場合、フィールド参照のみが不変であることに注意してください。オブジェクト参照が参照するものは変更可能であり、不変性に影響します。)
  • 最終的な静的フィールド-今は静的な最終フィールドを使用していた多くのケースで列挙型を使用しています。

検討するが慎重に使用する:

  • 最終クラス-私が検討するのは、フレームワーク/ API設計のみです。
  • 最終メソッド-基本的に最終クラスと同じです。クレイジーで最終的なものをマークするようなテンプレートメソッドパターンを使用している場合は、おそらく継承に依存しすぎていて、委任に十分ではありません。

肛門を感じない限り無視してください:

  • メソッドのパラメーターとローカル変数-怠惰でコードが雑然としていることが主な原因です。私が変更しないつもりのパラメータとローカル変数のマーキングが「より適切」であることを完全に認めます。それがデフォルトだったらいいのに。しかし、それはそうではなく、ファイナル全体でコードを理解するのはより難しいと感じています。私が他の誰かのコードにいる場合、私はそれらを引き出すつもりはありませんが、新しいコードを書いている場合、それらを入れません。1つの例外は、アクセスできるように何かを最終的にマークする必要がある場合です。匿名の内部クラス内から。

8
この最後のトピックに関する複数の質問で最良の回答を得る
Gabrielberbák2010年

4
ほとんどの場合、前に最後の単語がないローカル変数が表示され、そこで使用できない場合、最終的に作成する必要な値を返す新しいメソッドでコードを抽出する必要があることを示しています。これが当てはまらない場合は、いくつかのストリームを使用している場合、またはそれを試す必要がある場合です。
nyxz

32

finalキーワードを使用する前に、完全な使用法を理解する必要があります。変数、フィールド、メソッド、クラスに適用でき、異なる影響を及ぼします

詳細については、以下にリンクされている記事をチェックすることをお勧めします。

最後のキーワードの最後の単語


29

final特に変数の修飾子は、コンパイラーに、一般的に賢明な規則を適用させる手段です。つまり、(ローカルまたはインスタンス)変数が正確に1回だけ割り当てられるようにします(それ以上)。変数が使用される前に確実に割り当てられるようにすることで、NullPointerException次の一般的なケースを回避できます。

final FileInputStream in;
if(test)
  in = new FileInputStream("foo.txt");
else
  System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

変数が2回以上割り当てられるのを防ぐことにより、広範囲にわたるスコープを抑制することができます。これの代わりに:

 String msg = null;
 for(int i = 0; i < 10; i++) {
     msg = "We are at position " + i;
     System.out.println(msg);
 }
 msg = null;

これを使用することをお勧めします:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

いくつかのリンク:


1
+1は、広範にわたるスコープスコーピングの議論
トムトレサンスキー2010

つまり、基本的にはJavaをより式ベースfinalするために使用できます。
Adam Gent

実際には、「変数が使用される前に確実に割り当てられるようにすることで、NullPointerExceptionの一般的なケースを回避できます。」これは正しくありません。「最終」はここでは違いがありません。提供された例ではnullです。
nyholku

@nyholkuそうです、Javaでは、ローカル変数は使用前に必ず割り当てる必要があります。しかし、フィールドではファイナルが必要です。"in"がクラスのインスタンス変数であり、if / elseがコンストラクターにある、もう少し複雑な例では、finalが問題を通知する必要があります。さらに、ローカル変数の場合も、最終的なIMOに値があります。これは、ずさんなプログラマが、宣言に「= null」を追加することで「in」が未割り当てであるというコンパイルエラーを「修正」できないようにするためです。言い換えれば、私の経験では、最終的な変数はnullの使用を減らします。
Bruno De Fraine

20

私は可能なすべての変数を宣言することについてかなり独断的ですfinal。これには、メソッドパラメータ、ローカル変数、まれに値オブジェクトフィールドが含まれます。最終的な変数をどこでも宣言する主な理由は3つあります。

  1. 意図の宣言:最終的な変数を宣言することにより、この変数は一度だけ書き込まれることを意図していると述べています。他の開発者にとっては微妙なヒントであり、コンパイラにとっては大きなヒントです。
  2. 使い捨て変数の強制:私は、各変数の人生の目的は1つだけであるという考えを信じています。各変数に1つの目的のみを与えることにより、デバッグ中にその特定の変数の目的を調べるのにかかる時間を短縮できます。
  3. 最適化が可能:私は、コンパイラーが以前は変数参照の不変性に特に依存していたパフォーマンス強化のトリックを使用していたことを知っています。これらの古いパフォーマンストリック(または新しいトリック)のいくつかはコンパイラーによって使用されると思います。

ただし、最終的なクラスとメソッドは、最終的な変数参照ほど有用ではないと思います。finalこれらの宣言で使用されるキーワードは、単に自動テストとあなたが予想したことがない可能性があることな方法であなたのコードの使用に障害物を提供しています。


2
final変数とメソッドパラメータは、バイトコードで表現されないため、パフォーマンスに影響を与えません。
Steve Kuo 14

変数:=ラテン語:varius>さまざま>変更可能
ダイエット

17

効果的なJavaには、「不変オブジェクトを優先する」という項目があります。フィールドをfinalとして宣言すると、これに向けて小さな一歩を踏み出すのに役立ちますが、もちろんそれ以外にも、真に不変のオブジェクトにははるかに多くのものがあります。

オブジェクトが不変であることがわかっている場合は、同期の心配なしに多くのスレッド/クライアント間でオブジェクトを共有して読み取ることができ、プログラムの実行方法を推論するのは簡単です。


15
注意-finalとimmutableは完全に異なる概念です。最終的な可変オブジェクトを作成するのは簡単です-最終は参照についてであり、可変性はオブジェクトインスタンスについてです。final Person olaf = new Person(); olaf.setName( "Olaf");
Olaf Kock

1
正確には、不変オブジェクトは1つであり、不変参照(つまり、最終)は完全に別のものです。
SCdF 2008

2
ただし、Person.nameフィールドをfinalとして宣言する(およびクラスfinalを宣言するなど)ことにより、Personオブジェクトをfinalにすることができるため、これらは密接に関連しています。ただし、「最終的な人」を行うだけでは簡単ではありません...
Sebastian Ganslandt

これは、ローカル変数には関係ありません(パラメーターはローカルです)。これはクラス変数にのみ適用されるので、質問に部分的にしか対処しません。
Robin

12

変数に最後のキーワードを設定することで間違いを犯したことがないので、今のところ、それは大きな時間の無駄だと思います。

実行する本当の理由がない限り(その変数が最終的なものであると特定の点を説明したい場合など)、コードの可読性が低下することがわかったので、実行しないでください。

ただし、コードが読みづらくなったり、長く書けなくなったりする場合は、どうぞそれを試してください。

編集:明確にするために(そして、反対票を取り戻すための試みとして)、定数を最終としてマークしないでください、私は次のようなことをしないと言っています:

public String doSomething() {
  final String first = someReallyComplicatedExpressionToGetTheString();
  final String second = anotherReallyComplicatedExpressionToGetAnother();

  return first+second;
}

(私の意見では)コードを読みにくくするだけです。

また最後に行うことはすべて、変数を再割り当てできないようにすることであり、変数を不変にしたり、そのようなものにしたりすることはありません。


1
声明を正直に言うと、私は(そして一般的に不変のオブジェクトを)使用しないことがバグの数と影響に大きな影響を与えている多くの状況にありました。final
Chris Vest、

3
私はすべて不変のオブジェクトが好きです。不変のオブジェクトをfinalとマークすることが役に立ったという状況には、これまで一度も行ったことはありません。
SCdF 2008

2
ローカル変数とメソッドパラメーターにfinalを使用しないでください。コードが読みにくくなります。
rmaruszewski

@ChrisVest、多分あなたの関数は長すぎるかもしれません
Pacerier

8

finalは常に定数に使用する必要があります。変数を定義するためのルールが複雑な場合、(単一のメソッド内の)存続期間の短い変数に対しても役立ちます。

例えば:

final int foo;
if (a)
    foo = 1;
else if (b)
    foo = 2;
else if (c)
    foo = 3;
if (d)        // Compile error:  forgot the 'else'
    foo = 4;
else
    foo = -1;

6

最後のキーワードを使用することに対する最大の議論のように聞こえるのは、「それは不必要」であり、「スペースを浪費する」ということです。

ここの多くの素晴らしい投稿で指摘されているように、「ファイナル」の多くの利点を認め、タイピングとスペースが増えることを認めながら、Javaはデフォルトで変数を「ファイナル」にして、「コーダーがそれを望んでいる場合、「可変」。


1
ダウンボーター、説明してもいい?
RAY

それを変数と呼ぶのは奇妙に思えますね?
アラン

2
何千もの英語の単語から選択できます。
2014年

これは完璧です。final「コードが長すぎる」、「コードが不格好になる」以外の議論はありません。にはいくつかの引数がありますfinal。また、手動で入力したくない場合は、保存時に自動的に追加できます。
Dmitriy Popov

5

私はfinal常にオブジェクト属性に使用しています。

finalオブジェクトの属性に使用された場合のキーワードは、視認性の意味を持っています。基本的に、最終オブジェクト属性の値の設定は、コンストラクターが戻る前に行われます。これは、this参照をコンストラクターからエスケープせずfinalすべての属性に使用する限り、オブジェクトは(Java 5セマンティクスの下で)正しく構築されることが保証されており、不変であるため、安全に公開できることを意味します他のスレッドへ。

不変オブジェクトは、スレッドセーフだけではありません。また、変更可能なスペースは意図的に変更され、一貫して使用された場合、変更する必要のあるものだけに完全に制限されるため、プログラムの状態遷移を簡単に推論できます。

また、メソッドをfinalにすることもありますが、それほど頻繁ではありません。私はめったにクラスをファイナルにしません。私はほとんど必要がないので、私は一般的にこれを行います。私は通常、継承をあまり使用しません。代わりにインターフェイスとオブジェクト構成を使用することを好みます。これは、テストしやすい設計に適しています。具象クラスの代わりにインターフェースにコーディングする場合、jMockなどのフレームワークを使用すると、テスト時に継承を使用する必要がありません。具象クラスを使用するよりも、インターフェースを使用してモックオブジェクトを作成する方がはるかに簡単です。

クラスの大部分をファイナルにする必要があると思いますが、まだハビにはいっていません。


4

仕事のためにたくさんのコードを読まなければなりません。インスタンス変数のfinalが欠落していることは、私を困らせる一番の原因の1つであり、コードの理解を不必要に難しくしています。私のお金では、ローカル変数のfinalは明快さよりも混乱を引き起こしています。言語はそれをデフォルトにするように設計されている必要がありますが、私たちは間違いに耐えなければなりません。特にループやif-elseツリーでの明確な割り当ての場合に役立つことがありますが、ほとんどの場合、メソッドが複雑すぎることを示します。


煩雑さを軽減するツールを使用してみませんか?
パセリエ

@Pacerierコードを偽るエディタのように?
トム・ホーティン-タックライン

3

finalは明らかに定数に使用し、不変性を強制する必要がありますが、メソッドには別の重要な使用方法があります。

効果的なJavaには、これに関する全体の項目(項目15)があり、意図しない継承の落とし穴を指摘しています。継承のためにクラスを設計および文書化していない場合、効果的には、クラスから継承すると予期しない問題が発生する可能性があります(アイテムは良い例です)。したがって、継承を意図していないクラスやメソッドでfinalを使用することをお勧めします。

それは厳しいと思われるかもしれませんが、それは理にかなっています。他の人が使用するためにクラスライブラリを作成している場合は、そのために設計されていないものから継承したくない-後方互換性のために、クラスの特定の実装にロックされます。チームでコーディングしている場合、チームの別のメンバーが本当に必要場合にファイナルを削除するのを止めることはできません。しかし、キーワードは彼らに彼らが何をしているのかを考えさせ、継承元のクラスがそのために設計されたものではないことを警告しますので、彼らは特に注意する必要があります。


3

もう1つの注意点は、参照を変更できないのではなく、インスタンス変数の内容を変更できないことを最終的に混乱させる人が多いことです。


そして、この投稿はその大きな証拠です。
inigoD 2016

2

ローカル変数の場合でも、それが最終的に宣言されていることを知っていれば、後で参照が変更されることを心配する必要がありません。これは、デバッグ時にその変数を確認したときに、同じオブジェクトを参照していると確信していることを意味します。これは、バグを探すときに心配する必要があることの1つです。おまけは、99%の変数がfinalと宣言されている場合、実際に変数であるいくつかの変数がよりよく目立つということです。また、ファイナルを使用すると、コンパイラーは、他の方法では気付かれない可能性があるさらに可能性のある愚かな間違いを見つけることができます。


2

final各メソッドの各パラメーターに入力することを選択すると、コーダーとコードリーダーの両方にとって非常に苛立たしいことになります。

苛立ちが合理的なスイッチを超えたら、デフォルトで引数が最終的なScalaに切り替えます。

または、自動的にそれを行うコードスタイリングツールをいつでも使用できます。すべてのIDEには、それらが実装されているか、プラグインとして組み込まれています。


1

Javaで変数を使用すると、FinalはC ++の定数の代わりになります。したがって、finalおよびstaticが変数に使用されると、不変になります。同時に、移行されたC ++プログラマーをかなり満足させます;-)

参照変数と共に使用すると、オブジェクトを操作することはできますが、オブジェクトを再参照することはできません。

メソッドでfinalを使用すると、サブクラスによってメソッドをオーバーライドできなくなります。

使用法が非常に明確になったら、注意して使用する必要があります。メソッドでfinalを使用してもポリモーフィズムは役に立たないため、これは主に設計に依存します。

変数の値が決して変更されない、または変更されるべきでないと確信している場合にのみ、変数に使用してください。また、SUN.forの推奨するコーディング規則に従っていることを確認してください。例:final int COLOR_RED = 1; (アンダースコアで区切られた大文字)

参照変数では、特定のオブジェクトへの不変の参照が必要な場合にのみ使用します。

読みやすさの部分については、final修飾子を使用するときにコメントが非常に重要な役割を果たすようにします。


なぜこれが反対票を投じられるのか誰かが教えてもらえますか??? ちょうど好奇心が強い..
全能性

それはおそらく、XYZのときにのみ最終的にする必要があると述べているためです。他の人がそうする必要がある場合を除いて、すべてを最終的にすることがより良い実践であると考える人もいます。
アウトロープログラマ

1
C ++のconstキーワードの代わりにはなりません。-1。
tmj 2016年

1

私はローカル変数でそれらを使用することはありません。追加された冗長性にはほとんど意味がありません。変数を再割り当てする必要がないと考えている場合でも、次の人が別の方法でコードを変更することにはほとんど違いがありません。また、コードが変更されているため、最終的なものにするという元々の目的が無効になる場合があります。明確にするためだけの場合は、冗長性の悪影響のために失敗すると思います。

定数の場合を除いて、ほとんどメリットがないので、メンバー変数にもほとんど同じことが当てはまります。

また、不変であることの最良の指標は、そのように文書化されていること、および/またはオブジェクトを変更できるメソッドがないことです(これは、クラスをfinalにすることと一緒に、不変です)。

しかし、ちょっと、それは私の意見です:-)


1

変更されていないすべてのフィールドと属性にfinalを追加するようにEclipseを設定しました。これは、ファイルを保存するときにこれらの最終的な修飾子を(とりわけ)追加するEclipseの「保存アクション」を使用してうまく機能します。

強くお勧めします。

Eclipse Save Actionsに関する私のブログ投稿をチェックしください。


1

議論については、私はそれらは必要ではないと思います。モストリーは、単に読みやすさを損なうだけです。引数変数の再割り当てはめちゃくちゃ愚かなので、とにかく定数として扱うことができると確信しています。

Eclipseが最終的に赤く色付けされるという事実は、コード内の変数宣言を見つけやすくするため、ほとんどの場合、readbillityが改善されると思います。

私は、すべての変数がfinalでなければならないというルールを適用しようとします。そうでなければ、極端な正当な理由はありません。「この変数は何ですか?」と答える方がはるかに簡単です。初期化を見つけ、それがそれであると確信しなければならない場合は、質問してください。

私は実際に、今日、ファイナルではない変数についてかなり緊張しています。それは、ナイフが糸にぶら下がっているのか、それとも単にキッチンの引き出しがあるのか​​の違いのようです...

最後の変数は、ラベル可能な値へのちょうど良い方法です。

最終でない変数は、バグが発生しやすいアルゴリズムの一部にバインドされています。

優れた機能の1つは、ほとんどの場合、アルゴリズムの問​​題として変数を使用するオプションが、代わりにメソッドを記述することであり、通常はコードが大幅に改善されることです。


1

私はしばらくコーディングしていて、できる限りfinalを使用しています。これを(変数、メソッドパラメータ、およびクラス属性に対して)しばらく実行した後、私の変数の90%(またはそれ以上)が実際には最終であると言えます。変更したくないときに変数を変更しないことの利点(以前に見たことがありますが、それはときどき苦痛です)は、コード内の追加の入力と追加の "最終"キーワードの犠牲になります。

そうは言っても、言語を設計する場合、他のキーワードで変更しない限り、すべての変数をfinalにします。

クラスやメソッドにはfinalをあまり使わないと思いました。クラスがユーティリティクラスでない場合(この場合、プライベートコンストラクターは1つだけにする必要があります)、これは多かれ少なかれ複雑な設計の選択です。

また、Collections.unmodifiable ...を使用して、必要に応じて変更不可能なリストを作成します。


0

イベントリスナーなどに匿名ローカルクラスを使用することは、Javaの一般的なパターンです。finalキーワードの最も一般的な使用法は、スコープ内の変数に偶数リスナーがアクセスできるようにすることです。

ただし、コードに多くの最終ステートメントを配置する必要がある場合。それはあなたが何か間違ったことをしている良いヒントかもしれません。

上記の記事はこの例を示しています。

public void doSomething(int i, int j) {
    final int n = i + j; // must be declared final

    Comparator comp = new Comparator() {
        public int compare(Object left, Object right) {
            return n; // return copy of a local variable
        }
    };
}

0

内部および外部メソッドの定数に使用します。

(何らかの理由で)サブクラスが指定されたメソッドをオーバーライドしたくないかどうかわからないため、メソッドにそれを使用することがときどきあります。

クラスに関しては、一部のインフラストラクチャクラスについてのみ、ファイナルクラスを使用しました。

IntelliJ IDEAは、関数パラメーターが関数内に書き込まれると警告を出します。そのため、関数の引数にfinalを使用するのをやめました。Javaランタイムライブラリ内にも表示されません。


0

人々がそれらをオーバーライドできるようにしたいので、私はメソッドやクラスでfinalをほとんど使用しません。

それ以外の場合は、最終的にそれが public/private static final type SOME_CONSTANT;


うーん.. 1つの場所で編集されました..スティルは最終的に2行目に表示されます;-)
全能性

人々が値を上書きできるようにすることは、サプライズとバグを見つけ出すのが難しい単一の最大の原因です
RAY

0

クラスをfinalとマークすると、実行時ではなくコンパイル時にいくつかのメソッドバインディングが発生する可能性もあります。以下の "v2.foo()"を検討してください。コンパイラはBがサブクラスを持つことができないことを知っているため、foo()をオーバーライドできないため、呼び出す実装はコンパイル時にわかります。クラスBがfinalとマークされていない場合、v2の実際のタイプは、Bを拡張してfoo()をオーバーライドするクラスである可能性があります。

class A {
    void foo() {
        //do something
    }
}
final class B extends A {
    void foo() {
    }
}
class Test {
    public void t(A v1, B v2) {
        v1.foo();
        v2.foo();
    }
}

-1

定数に final 使用することを強くお勧めします。ただし、メソッドまたはクラスには使用しません(または少なくともしばらくは考えます)。これは、不可能ではないにしても、テストが困難になるためです。クラスまたはメソッドを確実にfinalにする必要がある場合は、このクラスが何らかのインターフェースを実装していることを確認してください。そうすれば、同じインターフェースを実装するモックを作成できます。


とにかくインターフェースを使用する必要があるため、テストが難しくなることはありません。
クリスベスト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.