Javaでは、パラメータとローカルに「最終」を使用する必要はありますか?


105

Javaでは、変数(フィールド/ローカル/パラメーター)をとしてマークしてfinal、それらへの再割り当てを防ぐことができます。一部の属性(またはクラス全体)が不変であるべきかどうかをすばやく確認できるので、フィールドで非常に便利です。

一方、ローカルおよびパラメーターではあまり役に立たないことがわかり、通常final、それらが再割り当てされないようにマークすることは避けます(内部クラスで使用する必要がある場合は明らかな例外があります) 。しかし、最近では、できる限りfinalを使用するコードに出会いました。これは技術的に詳細な情報を提供していると思います。

プログラミングスタイルに自信がなくなったので、finalどこにでも適用することのその他の長所と短所は何か、最も一般的な業界スタイルは何か、そしてその理由は何だろう。


@Amirコーディングスタイルの質問は、SOよりもここに属しているようで、FAQまたはこのサイトのメタでこれに関するポリシーを見つけることができませんでした。指示してもらえますか?
オーク

1
@Oakそれは言う:「特定のプログラミングの問題、ソフトウェアアルゴリズム、コーディングは、スタックオーバーフローに尋ねる」
アミールレザエイ

7
@アミール私は同意しませんが、これがどのようにコーディングの問題であるかわかりません。いずれにせよ、この議論はここに属さないため、この問題に関するメタトピックを開きました。
オーク

3
@amirこれは、プログラミングに関する主観的な質問であるため、このサイトのトピックに完全に載っています-/ faqを参照してください
ジェフアトウッド

1
再、ローカル変数:古いJavaコンパイラは、ローカルを宣言したかどうかに注意を払いfinal、それに応じて最適化しました。最新のコンパイラーは、それを自分で理解するのに十分スマートです。少なくともローカル変数についてfinalは、厳密には人間の読者のためです。あなたのルーチンがあまり複雑でないなら、ほとんどの人間の読者は自分自身でそれを理解できるはずです。
ソロモンスロー

回答:


67

私はfinalあなたと同じ方法を使用します。私にとっては、ローカル変数とメソッドのパラメーターは不要に見え、有用な追加情報を伝えません。

1つの重要なことは、メソッドを短く簡潔保ち、それぞれが1つのタスクを実行するよう努めることです。したがって、ローカル変数とパラメーターのスコープは非常に限定されており、1つの目的にのみ使用されます。これにより、それらを不注意に再割り当てする可能性が最小限に抑えられます。

さらに、ご存じのとおり、final(非プリミティブ)変数の値/状態を変更できないことを保証しません。一度初期化すると、そのオブジェクトへの参照を再割り当てできません。つまり、プリミティブ型または不変型の変数でのみシームレスに機能します。検討する

final String s = "forever";
final int i = 1;
final Map<String, Integer> m = new HashMap<String, Integer>();

s = "never"; // compilation error!
i++; // compilation error!
m.put(s, i); // fine

これは、多くの場合、コード内で何が起こっているのかを理解しやすくしないことを意味し、これを誤解すると、実際には検出しにくい微妙なバグが発生する可能性があります。


1
編集に関して-私は、セマンティクスを知っていfinalます、ありがとう:)が、短くてきれいなメソッドについての良い点-メソッドが十分に短く、変数が再割り当てされていないことが明らかであれば、finalキーワードを検討する動機はさらに低くなります。
オーク

最終的なパラメーターとローカル変数があり、それでも簡潔で簡潔な構文があれば素晴らしいと思いませんか?programmers.stackexchange.com/questions/199783/...
oberlies

7
「最終は、(非プリミティブ)変数の値/状態を変更できないことを保証しません。初期化されると、そのオブジェクトへの参照を再割り当てできないことだけが保証されます。」非プリミティブ=参照(Javaのタイプはプリミティブタイプと参照タイプのみです)。参照型の変数の値はある基準。したがって、参照を再割り当てすることはできません=値を変更することはできません。
user102008

2
参照/状態の落とし穴に+1。ただし、Java8の新しい機能的な側面でクロージャーを作成するには、変数にfinalをマークする必要がある場合があることに注意してください
Newtopian

@ user102008が指摘しているように、比較は偏っています。変数の割り当ては値の更新とは
異なり

64

Javaプログラミングのスタイルと考え方は問題ありません。自分自身を疑う必要はありません。

一方、ローカルおよびパラメーターではあまり役に立たず、通常、それらが再割り当てされない場合でも最終としてマークすることは避けます(内部クラスで使用する必要がある場合は明らかな例外があります) )。

これがまさにfinalキーワードを使用すべき理由です。あなたは、あなたはそれが決して再割り当てされることはないと知っているが、他の誰もそれを知らないことを述べる。finalすぐに使用すると、コードの曖昧さが解消されます。


8
あなたの方法が明確で、読者が知っていることを1つだけ行う場合。最後の言葉はコードを読めなくするだけです。そして、その読めないそのはるかに曖昧な場合
eddieferetro

4
@eddieferetro同意しない。キーワードにfinalは意図が示されているため、コードが読みやすくなります。また、実際のコードを処理しなければならない場合、それはめったに手付かずで明確であることに気付くでしょうfinal。可能な限りどこにでもs を自由に追加すると、バグを見つけてレガシーコードをよりよく理解するのに役立ちます。
アンドレスF.

4
変数は決して変わらないという「意図」であり、コードはその事実に依存していますか?または、「この変数が変更されないように起こるので、最終的にマークします」という意図があります。後者は役に立たず、おそらく有害なノイズです。そして、あなたはすべての地元の人々をマークすることを提唱しているように見えるので、あなたは後でやっています。ビッグ-1。
user949300

2
final意図を述べています。これは通常、コードの一部では良いことですが、視覚的な混乱を追加する代償が伴います。プログラミングのほとんどのことと同様に、ここでもトレードオフがあります。変数は、追加された冗長性に見合うだけの1度だけ使用されるという事実を表現していますか?
クリストフム

30

final/をconst可能な限り使用することの1つの利点は、コードの読者の精神的な負担を軽減することです。

値/参照が後で変更されることはないので、彼は安心できます。そのため、計算を理解するために変更に注意を払う必要はありません。

純粋に機能的なプログラミング言語を学んだ後、私はこれに関して私の考えを変えました。「変数」が常に初期値を保持することを信頼できるなら、なんてほっとするでしょう。


16
さて、Java finalでは(非プリミティブ)変数の値/状態を変更できないことを保証しません。一度初期化すると、そのオブジェクトへの参照を再割り当てできません。
ペテルトレック

9
だからこそ、価値と参照を区別したのです。この概念は、不変のデータ構造および/または純粋な機能のコンテキストで最も有用です。
レニープログラマー

7
@PéterTörökプリミティブ型ではすぐに役立ちますが、可変オブジェクトへの参照では多少役立ちます(少なくとも同じオブジェクトを常に扱っていることはわかっています!)。最初から不変であるように設計されたコードを扱うとき、それは非常に役立ちます。
アンドレスF.

18

finalメソッドのパラメーターとローカル変数では、コードノイズと見なします。Javaメソッドの宣言は非常に長くなる可能性があります(特にジェネリックの場合)-もう宣言する必要はありません。

単体テストが適切に記述されている場合、「有害」なパラメータへの割り当てが選択されるため、実際に問題なることはありません。ユニットテストのカバレッジが不十分であるため、検出されない可能性のあるバグを回避するよりも、視覚的な明瞭さが重要です。

FindBugsやCheckStyleのようなツールは、パラメーターやローカル変数に割り当てが行われた場合にビルドを中断するように構成できます。そのようなことを深く気にかけている場合。

もちろん、たとえば匿名クラスで値を使用しているなどの理由で、それらを最終的にする必要がある場合は、問題ありません。これが最も簡単でクリーンなソリューションです。

パラメータに余分なキーワードを追加し、それによってそれらを偽装するという明らかな効果とは別に、メソッドパラメータにfinalを追加すると、メソッド本体のコードが読みにくくなり、コードが悪化します-「良い」コードできるだけ読みやすく、シンプルでなければなりません。不自然な例として、大文字と小文字を区別せずに動作させる必要があるメソッドがあるとします。

なしfinal

public void doSomething(String input) {
    input = input.toLowerCase();
    // do a few things with input
}

シンプル。掃除。誰もが何が起こっているかを知っています。

「最終」オプション1を使用します。

public void doSomething(final String input) {
    final String lowercaseInput = input.toLowerCase();
    // do a few things with lowercaseInput
}

パラメータを作ることながら、finalさらに下に、彼は元の値での作業の思考からのコードを追加することコーダを停止し、さらに下にそのコードを使用することができます等しい危険がありますinput代わりにlowercaseInput、それはやから保護することができないべきではない、「ので、あなたがすることができ、 tはスコープの外にそれを取る(あるいは割り当てるnullinputそれでもとにかく助けになる場合)。

「最終」オプション2の場合:

public void doSomething(final String input) {
    // do a few things with input.toLowerCase()
}

これで、さらに多くのコードノイズが作成され、toLowerCase()n回呼び出す必要があるというパフォーマンスヒットが発生しました。

「最終」、オプション3の場合:

public void doSomething(final String input) {
    doSomethingPrivate(input.toLowerCase());
}

/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
    if (!input.equals(input.toLowerCase())) {
        throw new IllegalArgumentException("input not lowercase");
    }
    // do a few things with input
}

コードノイズについて話します。これは列車事故です。他のコードが誤って呼び出す可能性があるため、必要な例外ブロックという新しいメソッドがあります。例外をカバーするためのより多くの単体テスト。すべてが1つの単純な、そして私見が望ましい無害なラインを避けるために。

また、メソッドを視覚的に簡単に取り入れることができず、パラメーターへの割り当てが行われたことを一目で知ることができないほど、メソッドを長くすべきではないという問題もあります。

私はそれはあなたがそれを行うパラメータに割り当てた場合、好ましくは最初の行またはストレート基本的な入力チェックの後、この方法では、すべての初期のが効果的という良い習慣/スタイルだと思いますか交換のためにそれを全体の中に一貫性のある効果を持っている方法で、方法。読者は、割り当てが明白(署名宣言の近く)で一貫した場所にあることを期待していることを知っています。実際、パラメーターに割り当てることはめったにありませんが、そうする場合は常にメソッドの最上部で行います。


また、final実際には保護されていないことに注意してください。

public void foo(final Date date) {
    date.setTime(0); 
    // code that uses date
}

final パラメータタイプがプリミティブまたは不変でない限り、完全に保護されません。


最後のケースでfinalは、同じDateインスタンスを扱っているという部分的な保証を提供します。いくつかの保証は、何よりも優れています(もしあれば、不変のクラスを一から議論しています!)。いずれにせよ、実際には、あなたが言うことの多くは、既存の不変のデフォルト言語に影響するはずですが、影響はありません。つまり、それは問題ではないということです。
アンドレスF.

「コードがさらに下にあると入力が使用される可能性がある」というリスクを示すために+1。それでも、final上記のような場合にパラメータを最終的にしない可能性があるので、パラメータをにすることをお勧めします。キーワードで署名をスパムするだけの価値はありません。
-maaartinus

7

finalプログラムを読みやすくするために、Eclipseを各ローカル変数の前に置いています。パラメーターリストはできるだけ短くしたいので、パラメーターで作成しません。理想的には1行に収まるようにします。


2
また、パラメーターについては、パラメーターが割り当てられている場合、コンパイラーに警告またはエラーを発行させることができます。
服従

3
まったく逆に、コードが乱雑になるため、読みにくくなります。
スティーブクオ

3
@Steve Kuo:すべての変数をすばやく見つけることができます。大きな利益はありませんが、偶発的な割り当てを防ぐことと合わせて、6文字の価値があります。var非最終変数をマークするようなものがあれば、もっとうれしいです。YMMV。
maaartinus

1
@maaartinus約合意var!残念ながら、これはJavaのデフォルトではなく、今では言語を変更するには遅すぎます。したがって、私は書くことの軽微な不便さに我慢しますfinal:)
Andres F.

1
@maaartinus同意しました。初期化後、(ローカル)変数の約80〜90%を変更する必要はありません。したがって、それらの80〜90%にはfinalキーワードが追加されますが、10〜20%のみが必要varです。
JimmyB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.