可能な限り使用することを提案する参考文献(たとえば)をいくつか見つけましたが、final
それがどれほど重要か疑問に思っています。これは主に、メソッドパラメータとローカル変数のコンテキストであり、最終的なメソッドやクラスではありません。定数の場合、それは当然のことです。
一方では、コンパイラーはいくつかの最適化を行うことができ、プログラマーの意図をより明確にします。一方、冗長性が追加され、最適化は簡単な場合があります。
覚えておくべきなのでしょうか?
可能な限り使用することを提案する参考文献(たとえば)をいくつか見つけましたが、final
それがどれほど重要か疑問に思っています。これは主に、メソッドパラメータとローカル変数のコンテキストであり、最終的なメソッドやクラスではありません。定数の場合、それは当然のことです。
一方では、コンパイラーはいくつかの最適化を行うことができ、プログラマーの意図をより明確にします。一方、冗長性が追加され、最適化は簡単な場合があります。
覚えておくべきなのでしょうか?
回答:
にこだわる:
検討するが慎重に使用する:
肛門を感じない限り無視してください:
メソッドのパラメーターとローカル変数-怠惰でコードが雑然としていることが主な原因です。私が変更しないつもりのパラメータとローカル変数のマーキングが「より適切」であることを完全に認めます。それがデフォルトだったらいいのに。しかし、それはそうではなく、ファイナル全体でコードを理解するのはより難しいと感じています。私が他の誰かのコードにいる場合、私はそれらを引き出すつもりはありませんが、新しいコードを書いている場合、それらを入れません。1つの例外は、アクセスできるように何かを最終的にマークする必要がある場合です。匿名の内部クラス内から。
編集:@ adam-gentで言及されているように、最終的なローカル変数が実際に非常に役立つユースケースの1つは、値がif
/ else
ブランチのvarに割り当てられる場合です。
final
をデフォルトで作成します。ただし、本当に最新のJava IDE(つまり、IDEA)を使用しない限り、そのメリットに気付かない場合があります。
final
クラス/メソッド内の未使用/不要なコードを検出できます。たとえば、最終メソッドがチェック済み例外をスローするように宣言しているが、実際にはスローしない場合、IDEAはそれを通知し、throws
節から例外を削除できます。時には、未使用のメソッド全体を見つけることもできます。これは、オーバーライドできないときに検出可能です。
覚えておくべきことはありますか?
いいえ。Eclipseを使用している場合は、保存アクションを構成して、これらの最終修飾子を自動的に追加できます。次に、少ない労力でメリットを得ます。
「最終」の開発時の利点は、少なくとも実行時の利点と同じくらい重要です。それはあなたの意図についてコードの将来の編集者に何かを伝えます。
クラスに「最終」のマークを付けると、クラスの設計または実装中に拡張を適切に処理するための努力をしなかったことを示します。読者がクラスに変更を加えることができ、「最終」修飾子を削除したい場合は、自己の責任において行うことができます。クラスが拡張機能を適切に処理できるかどうかは、それらの責任です。
変数に「final」のマークを付ける(およびコンストラクターで割り当てる)と、依存関係の注入に役立ちます。変数の「コラボレーター」の性質を示します。
メソッドに「final」のマークを付けると、抽象クラスで役立ちます。拡張ポイントがどこにあるかを明確に示します。
私はfinal
いつもJavaをより表現ベースにするために使用しています。Javaの条件(if,else,switch
)は式に基づいていないので、関数型プログラミング(ML、Scala、Lispなど)に慣れている場合は特に嫌いです。
したがって、条件を使用するときは常に(IMHO)最終変数を使用するようにしてください。
例を挙げましょう。
final String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
break;
case JOB_POSTING_IMPORT:
name = "Blah";
break;
default:
throw new IllegalStateException();
}
ここで、別のcase
ステートメントを追加して設定しないとname
、コンパイラは失敗します。(変数を設定した)すべてのケースで中断しないと、コンパイラーも失敗します。これにより、JavaをLispのlet
式に非常に似たものにすることができ、コードが過度にインデントされないようにします(字句スコープ変数のため)。
そして、@ Recurseが述べたように(しかし私にはどうやら-1私)String name
final
、コンパイラエラーを取得することなく上記のことを行うことができます(私はあなたができなかったとは決して言えません)が、簡単にコンパイラエラーがスイッチの後に名前を設定するのをやめることができました式のセマンティクスをbreak
破棄するステートメント、または(@Recurseの内容にかかわらず)エラーを発生させずに、final
次のことを忘れると、
String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
//break; whoops forgot break..
//this will cause a compile error for final ;P @Recurse
case JOB_POSTING_IMPORT:
name = "Blah";
break;
}
// code, code, code
// Below is not possible with final
name = "Whoops bug";
バグ設定名break
が原因で(別のバグも忘れるだけでなく)、誤ってこれを行うことができます。
String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
break;
//should have handled all the cases for pluginType
}
// code, code, code
// Below is not possible with final
name = "Whoops bug";
最後の変数は、名前がどうあるべきかについて単一の評価を強制します。戻り値を持つ関数が常に値を返す必要がある方法と同様に(例外を無視して)、名前切り替えブロックは名前を解決する必要があるため、そのスイッチブロックにバインドされ、コードのチャンクのリファクタリングが容易になります(つまり、Eclipeリファクタリング:抽出メソッド)。 。
上記のOCaml:
type plugin = CandidateExport | JobPostingImport
let p = CandidateExport
let name = match p with
| CandidateExport -> "Candidate Stuff"
| JobPostingImport -> "Blah" ;;
match ... with ...
関数すなわち式のように評価します。switchステートメントのように見えることに注意してください。
以下は、Scheme(RacketまたはChicken)の例です。
(define name
(match b
['CandidateExport "Candidate Stuff"]
['JobPostingImport "Blah"]))
else if (...)
したときにこれが起こるのを見ましたif(...)
。私は、最終的な変数では決して起こらないであろうことを彼に示しました。基本的final
には、変数を1回だけ割り当てるように強制します... so:P
final
問題のメソッドが数ページにわからないほど混乱している場合に、リファクタリングの補助として役立つメソッドパラメータとローカルのマーキングを見つけました。final
自由に振りかけ、コンパイラ(またはIDE)がスローする「最終変数に割り当てられない」エラーを確認します。いくつかの(古い)コメントで、「data」という変数がnullになる理由がわかる場合があります。起こらない。
次に、再利用された変数を、使用場所の近くで宣言された新しい変数に置き換えることで、いくつかのエラーを修正できます。次に、メソッドのすべての部分をスコープブレースで囲むことができます。突然、「抽出メソッド」から離れた1つのIDEキーを押すだけになり、モンスターがより理解しやすくなりました。
あなたの方法がまだ維持不可能な問題でない場合、私は人々がそれをその問題に変えるのを思いとどまらせるようなものを最終的にすることに価値があるかもしれないと思います。しかし、それが短い方法である場合(参照:保守不可能ではない)、多くの冗長性を追加するリスクがあります。特に、Java関数のシグニチャーは、引数ごとに6つ追加することなく、80文字に収まるほど十分に困難です。
final
before beforeパラメータを使用しないと読みやすさが向上します。
まあ、これはすべてあなたのスタイルに依存します...変数を変更しないときにファイナルを見るのが好きなら、それを使用してください。見たくない場合は...
個人的には、できるだけ冗長性を抑えたいので、あまり必要のない余分なキーワードは使わないようにしています。
でも私は動的言語を好むので、冗長を避けたいのは当然のことです。
だから、私はあなたが傾いている方向を選んでそれと一緒に行くだけだと思います(いずれにしても、一貫性を保つようにしてください)。
余談ですが、私はそのようなパターンを使用するプロジェクトと使用しないプロジェクトの両方で作業しましたが、バグやエラーの量に違いはありませんでした。バグの数などを改善しますが、これもスタイルです。変更しないという意図を表明したい場合は、先に進んで使用してください。
パラメータで誤ってパラメータ値を変更することを避け、微妙なバグを導入するのに役立ちます。私はこの推奨を無視するために使用していますが、約4時間を費やした後です。恐ろしい方法で(何百行ものコードと複数のfors、ネストされたifsとあらゆる種類の悪い慣行で)私はそれを行うことをお勧めします。
public int processSomethingCritical( final int x, final int y ){
// hundreds of lines here
// for loop here...
int x2 = 0;
x++; // bug aarrgg...
// hundreds of lines there
// if( x == 0 ) { ...
}
もちろん、完璧な世界ではこれは起こりませんが、他のコードをサポートする必要がある場合もあります。:(
これが明白であるかどうかの質問からは明らかではありませんが、メソッドパラメーターをfinalにしても、メソッドの本体にのみ影響します。メソッドの意図に関する興味深い情報を呼び出し元に伝えることはありません。渡されるオブジェクトはメソッド内で変更でき(最終はconstではありません)、変数のスコープはメソッド内にあります。
あなたの正確な質問に答えるために、コードがそれを必要としない限り(例:変数が内部クラスから参照されている場合)、またはいくつかの本当に複雑なロジックを明確にするために、インスタンスまたはローカル変数(メソッドパラメーターを含む)をfinalにしたりしません。
インスタンス変数の場合、それらが論理的に定数であれば、それらをfinalにします。
変数には多くの用途がありますfinal
。ここにいくつかあります
最終定数
public static class CircleToolsBetter {
public final static double PI = 3.141;
public double getCircleArea(final double radius) {
return (Math.pow(radius, 2) * PI);
}
}
これは、コードの他の部分に使用したり、他のクラスからアクセスしたりできるため、値を変更する場合でも、1つずつ変更する必要はありません。
最終変数
public static String someMethod(final String environmentKey) {
final String key = "env." + environmentKey;
System.out.println("Key is: " + key);
return (System.getProperty(key));
}
}
このクラスでは、パラメーターenvironmentKeyにプレフィックスを追加するスコープ付き最終変数を作成します。この場合、最後の変数は、メソッドの実行ごとに異なる実行スコープ内でのみfinalになります。メソッドに入るたびに、ファイナルが再構築されます。作成されるとすぐに、メソッド実行のスコープ中に変更することはできません。これにより、メソッドの期間中、メソッド内の変数を修正できます。下記参照:
public class FinalVariables {
public final static void main(final String[] args) {
System.out.println("Note how the key variable is changed.");
someMethod("JAVA_HOME");
someMethod("ANT_HOME");
}
}
最終定数
public double equation2Better(final double inputValue) {
final double K = 1.414;
final double X = 45.0;
double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;
if (result > 360) {
powInputValue = X * Math.sin(result);
} else {
inputValue = K * Math.sin(result); // <= Compiler error
}
これらは、コードの行が非常に長い場合に特に役立ち、コンパイラエラーを生成するので、変更してはならない変数を誤って変更したときにロジック/ビジネスエラーが発生することはありません。
最終コレクション
コレクションについて話している場合とは異なり、変更不可として設定する必要があります。
public final static Set VALID_COLORS;
static {
Set temp = new HashSet( );
temp.add(Color.red);
temp.add(Color.orange);
temp.add(Color.yellow);
temp.add(Color.green);
temp.add(Color.blue);
temp.add(Color.decode("#4B0082")); // indigo
temp.add(Color.decode("#8A2BE2")); // violet
VALID_COLORS = Collections.unmodifiableSet(temp);
}
それ以外の場合、変更不可として設定しないと、次のようになります。
Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler
最終クラスと最終メソッドは、それぞれ拡張または上書きできません。
編集:カプセル化に関する最終的なクラスの問題に対処するには:
クラスをファイナルにする方法は2つあります。1つ目は、クラス宣言でキーワードfinalを使用することです。
public final class SomeClass {
// . . . Class contents
}
クラスをfinalにする2番目の方法は、すべてのコンストラクターをプライベートとして宣言することです。
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 . . .
}
}
残念ながら、クラスの唯一のコンストラクターはプライベートなので、このクラスを拡張することは不可能です。Testクラスの場合、クラスがfinalである必要はありません。Testクラスは、暗黙の最終クラスが問題を引き起こす可能性のある良い例です。
そのため、コンストラクターをプライベートにすることでクラスを暗黙的にfinalにする場合は、finalとマークする必要があります。
内部(匿名)クラスがあり、メソッドが包含メソッドの変数にアクセスする必要がある場合は、その変数をfinalにする必要があります。
それ以外は、あなたの言ったことは正しいです。
final
変数を次のように作成する場合は、変数にキーワードを使用しますimmutable
変数をfinalとして宣言することにより、高度なマルチスレッド環境での変数の可能な変更の問題を除外するのに役立ちます。
Java 8リリースでは、「effectively final variable
」と呼ばれるもう1つのコンセプトがあります。非最終変数は、最終変数として盛り上がる可能性があります。
ラムダ式から参照されるローカル変数はfinalまたは事実上finalでなければなりません
変数は、ローカルブロックでの初期化後に変更されない場合、有効な最終版と見なされます。これは、匿名クラスまたはラムダ式内でfinalキーワードなしでローカル変数を使用できることを意味します。ただし、それらは事実上finalでなければなりません。
Java 7までは、匿名クラス内でfinal以外のローカル変数を使用できませんが、Java 8からはできます
まず、最後のキーワードは変数を定数にするために使用されます。一定とは、変化しないことを意味します。例えば:
final int CM_PER_INCH = 2.54;
1インチあたりのセンチメートルは変化しないため、変数finalを宣言します。
最終値をオーバーライドしようとすると、変数は最初に宣言されたものになります。例えば:
final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"
次のようなコンパイルエラーがあります。
local variable is accessed from inner class, must be declared final
変数をfinalと宣言できない場合、またはfinalと宣言したくない場合は、以下を試してください。
final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);
これは印刷されます:
Hello World!
A String