次のようなメソッドによく遭遇します。
public void foo(final String a, final int[] b, final Object1 c){
}
最終パラメータを渡さずにこのメソッドを呼び出すとどうなりますか。つまり、後で変更された (したがって final として宣言されていない) Object1 は、このメソッドに問題なく渡すことができます。
Javaは、パラメーターをメソッドに送信する前に、常にパラメーターのコピーを作成します。これは、ファイナルが呼び出し元のコードに違いを意味しないことを意味します。これは、メソッド内で変数を再割り当てできないことを意味するだけです。
最終オブジェクトがある場合でも、オブジェクトの属性を変更できることに注意してください。これは、Javaのオブジェクトが実際にはオブジェクトへのポインタであるためです。そして、実際のオブジェクトではなく、ポインタのみがコピーされます(そして、メソッドの最後になります)。
final として宣言する必要がある状況があります。そうしないと、コンパイル エラーが発生します。つまり、それらを無名クラスに渡します。基本的な例:
public FileFilter createFileExtensionFilter(final String extension) {
FileFilter fileFilter = new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(extension);
}
};
// What would happen when it's allowed to change extension here?
// extension = "foo";
return fileFilter;
}
修飾子を削除するfinal
と、値がランタイム定数であることが保証されなくなるため、コンパイル エラーが発生します。つまり、匿名クラスの外部から値を変更すると、匿名クラスのインスタンスが作成後に異なる動作をすることになります。
Javaは値渡しのみです。(またはより良い-値による参照の受け渡し)
したがって、渡された引数とメソッド内の引数は、同じオブジェクト(値)を指す2つの異なるハンドラーです。
したがって、オブジェクトの状態を変更すると、それを参照している他のすべての変数に反映されます。ただし、新しいオブジェクト(値)を引数に再割り当てした場合、このオブジェクト(値)を指す他の変数は再割り当てされません。
The final
keyword on a method parameter means absolutely nothing to the caller. It also means absolutely nothing to the running program, since its presence or absence doesn't change the bytecode. It only ensures that the compiler will complain if the parameter variable is reassigned within the method. That's all. But that's enough.
Some programmers (like me) think that's a very good thing and use final
on almost every parameter. It makes it easier to understand a long or complex method (though one could argue that long and complex methods should be refactored.) It also shines a spotlight on method parameters that aren't marked with final
.
この foo() の実装を考えてみましょう:
public void foo(final String a) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.print(a);
}
});
}
Runnable
インスタンスはメソッドよりも長く存続するため、これはfinal
キーワードなしではコンパイルされません --final
参照のコピーを取得しても安全であることをコンパイラに伝えます (後で参照するため)。したがって、valueではなく、 final と見なされるのは参照です。言い換えれば、発信者として、何も台無しにすることはできません...
finalは、一度割り当てられた変数の値を変更できないことを意味します。
一方、これらのメソッドの引数にfinalを使用すると、メソッドの実行中にプログラマが値を変更できなくなります。これは、メソッド内で最終変数を再割り当てできないことを意味するだけです。
文字列は不変であるため、実際には後で文字列を変更することはできません (文字列オブジェクトを保持していた変数が別の文字列オブジェクトを指すようにすることしかできません)。
final
ただし、それが変数をパラメーターにバインドできる理由ではありません。すべてのコンパイラ チェックは、パラメーターがメソッド内で再割り当てされていないことです。これはドキュメンテーションの目的に適しており、間違いなく良いスタイルであり、速度のためにバイトコードを最適化するのにも役立ちます (ただし、これは実際にはあまり効果がないようです)。
ただし、メソッド内でパラメーターを再割り当てしても、Java はすべてのパラメーターを値渡しするため、呼び出し元はそれに気づきません。シーケンスの後
a = someObject();
process(a);
a のフィールドは変更されている可能性がありますが、 a は以前と同じオブジェクトのままです。参照渡し言語では、これは当てはまらない場合があります。