web-dev-qa-db-ja.com

Javaのメソッドパラメータでキーワード「final」を使用する必要があるのはなぜですか?

finalキーワードが本当にメソッドパラメーターで使用される場合に便利な場所がわかりません。

匿名クラスの使用、読みやすさ、意図の宣言を除外する場合、ほとんど価値がありません。

一部のデータを一定に保つことは、見かけほど強力ではありません。

  • パラメーターがプリミティブの場合、パラメーターとして値がメソッドに渡され、パラメーターを変更しても範囲外では効果がないため、パラメーターは効果がありません。

  • 参照によってパラメーターを渡す場合、参照自体はローカル変数であり、参照がメソッド内から変更された場合、メソッドスコープ外からの影響はありません。

以下の簡単なテスト例を検討してください。このテストはパスしますが、メソッドは与えられた参照の値を変更しましたが、効果はありません。

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}
354
Yotam Gilboa

変数が変更されないことを(読みやすさのために)明示するのはいいことです。以下に、finalを使用して頭痛の種を軽減できる簡単な例を示します。

public void setTest(String test) {
    test = test;
}

セッターで「this」キーワードを忘れた場合、設定する変数は設定されません。ただし、パラメーターでfinalキーワードを使用した場合は、コンパイル時にバグがキャッチされます。

217
Jerry Sha

変数の再割り当てを停止する

これらの答えは知的に興味深いものですが、短い簡単な答えは読んでいません。

コンパイラーが変数を別のオブジェクトに再割り当てしないようにするには、キーワードfinalを使用します。

変数が静的変数、メンバー変数、ローカル変数、または引数/パラメーター変数のいずれであっても、効果はまったく同じです。

実際の効果を見てみましょう。

2つの変数(argおよびx)の両方に異なるオブジェクトを再割り当てできる、この単純な方法を検討してください。

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

ローカル変数をfinalとしてマークします。これにより、コンパイラエラーが発生します。

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

代わりに、パラメータ変数をfinalとしてマークしましょう。これもコンパイラエラーになります。

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

この話の教訓:

変数が常に同じオブジェクトを指すようにする場合は、変数finalをマークします。

引数を再割り当てしない

プログラミングの良い習慣として(どの言語でも)、決して呼び出しメソッドによって渡されたオブジェクト以外のオブジェクトにパラメーター/引数変数を再割り当てする必要があります。上記の例では、arg =という行を決して書かないでください。人間は間違いを犯し、プログラマは人間であるため、コンパイラに支援してもらいましょう。コンパイラがそのような再割り当てを見つけてフラグを立てられるように、すべてのパラメータ/引数変数を「最終」としてマークします。

振り返ってみると

他の回答で述べたように、プログラマが配列の終わりを過ぎて読むなどの愚かな間違いを避けるためのJavaの元の設計目標を考えると、Javaはすべてのパラメータ/引数変数を「最終'。つまり、引数は変数であってはなりません。しかし、後知恵は20/20のビジョンであり、Javaデザイナーはその時に手をいっぱいにしていました。

完全性のために追加された別のケース

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }
213
Basil Bourque

はい、匿名クラス、読みやすさ、意図の宣言を除くと、ほとんど価値がありません。これらの3つのことは価値がありますか?

個人的には、匿名の内部クラスで変数を使用していない限り、ローカル変数とパラメーターにfinalを使用しない傾向がありますが、パラメーター値自体が変更されないことを明確にしたい人のポイントを確かに見ることができます参照するオブジェクトがその内容を変更した場合でも)。それが読みやすさを増すと思う人にとっては、それは完全に合理的なことだと思います。

誰かが実際にそれを主張している場合、あなたのポイントはより重要になりますdidデータがそうでない方法で一定に保つ-しかし、私はそのような主張を見たことを覚えていません。 finalには実際よりも多くの効果があることを示唆している開発者の大部分があることを提案していますか?

編集:私は本当にこれらすべてをMonty Pythonリファレンスでまとめたはずです。質問は、「ローマ人が私たちのために何をしたことがありますか?」

126
Jon Skeet

have finalを使用する1つのケースについて少し説明しましょう。

メソッドで匿名内部クラスを作成し、そのクラス内でローカル変数(メソッドパラメーターなど)を使用する場合、コンパイラーはパラメーターを最終的にするように強制します。

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

ここで、fromおよびtoパラメーターはfinalである必要があるため、匿名クラス内で使用できます。

その要件の理由は次のとおりです。ローカル変数はスタック上に存在するため、メソッドが実行されている間のみ存在します。ただし、匿名クラスインスタンスはメソッドから返されるため、より長く存続する可能性があります。後続のメソッド呼び出しに必要なため、スタックを保持することはできません。

したがって、代わりにJavaは、これらのローカル変数のcopiesを隠しインスタンス変数として匿名クラスに配置します(バイトコードを調べると確認できます)。しかし、それらが最終的なものではない場合、匿名クラスとメソッドが変数に変更を加えることを期待するかもしれません。 2つのコピーではなく1つの変数しかないという幻想を維持するために、それは最終的なものでなければなりません。

75

パラメーターには常にfinalを使用します。

それだけ追加しますか?あんまり。

オフにしますか?いや.

理由:私は、人々がずさんなコードを書いて、アクセサにメンバー変数を設定するのに失敗した3つのバグを見つけました。すべてのバグを見つけるのは難しいことが判明しました。

これが将来のバージョンのJavaでデフォルトになるようにしたいと思います。値渡し/参照渡しは、非常に多くのジュニアプログラマーをつまずかせます。

もう1つ..私のメソッドにはパラメーターの数が少ない傾向があるため、メソッド宣言の余分なテキストは問題になりません。

25
Fortyrunner

メソッドパラメータでfinalを使用することは、呼び出し側の引数に何が起こるかには関係ありません。そのメソッド内で変更されていないことを示すためのものです。より機能的なプログラミングスタイルを採用しようとすると、その価値が見えてきます。

18
Germán

個人的には、メソッドパラメーターにfinalを使用しません。パラメーターリストに煩雑になるためです。 Checkstyleのような方法でメソッドパラメータが変更されないように強制することを好みます。

可能な場合はいつでもfinalを使用するローカル変数については、個人プロジェクトのセットアップでEclipseにそれを自動的に行わせます。

私は確かにC/C++ constのようなより強力なものが欲しいです。

8
starblue

Javaは引数のコピーを渡すので、その関連性はかなり制限されていると感じています。これは、const char const *を実行することで、参照コンテンツの変更を禁止できるC++時代から来ていると思います。この種のものは、開発者がf ***のように本質的に愚かであり、彼が入力するすべての文字から保護する必要があると信じているように感じます。誰かが私のメソッドやものをオーバーライドしたくない場合を除いて、finalを許可していなくても、私はすべての謙虚さで、私は非常に少数のバグを書くと言わなければなりません...たぶん私はただ昔ながらの開発者です...:- O

4
Lawrence

簡単な答え:finalは少し役立ちますが、代わりにクライアント側で防御的なプログラミングを使用してください。

確かに、finalの問題は、referenceが変更されず、参照オブジェクトのメンバーが呼び出し側に知られないように変更されることを喜んで許可することだけです。したがって、この点でのベストプラクティスは、呼び出し側での防御的プログラミングであり、不正なAPIによって強奪される危険にさらされているオブジェクトの深く不変のインスタンスまたは深いコピーを作成します。

1
Darrell Teague

パラメータリストでfinalを使用することはありません。以前の回答者が言ったように、混乱を招くだけです。また、Eclipseでは、パラメーターの割り当てを設定してエラーを生成することができるため、パラメーターリストでfinalを使用することはかなり冗長なようです。おもしろいことに、パラメータ割り当てのEclipse設定を有効にしてエラーを生成すると、このコードがキャッチされました(実際のコードではなく、フローを覚えているだけです)。

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

悪魔の擁護者を演じる、これを行うことで正確に何が悪いのでしょうか?

1
Chris Milburn

パラメーター宣言にfinalを追加するもう1つの理由は、「メソッドの抽出」リファクタリングの一部として名前を変更する必要がある変数を識別するのに役立つことです。大規模なメソッドリファクタリングを開始する前に各パラメーターにfinalを追加すると、続行する前に対処する必要がある問題があるかどうかがすぐにわかります。

ただし、通常は、リファクタリングの最後に余分なものとして削除します。

0