web-dev-qa-db-ja.com

メソッドパラメータとローカル変数にfinalを使用する必要があるのはいつですか?

可能な限りfinalを使用することを示唆するいくつかの参照( たとえば )を見つけましたが、それがどれほど重要か疑問に思います。これは主にメソッドパラメータとローカル変数のコンテキストであり、最終的なメソッドやクラスではありません。定数については、当然のことです。

一方では、コンパイラーはいくつかの最適化を行うことができ、プログラマーの意図を明確にします。一方、冗長性が追加され、最適化は簡単になります。

覚えておくべき努力ですか?

159
eaolson

強迫観念:

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

考慮しますが、慎重に使用してください。

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

肛門を感じない限り無視:

  • メソッドのパラメーターとローカル変数-私は怠け者でコードが乱雑だと思うので、これを行うことはめったにありません。変更しないパラメーターとローカル変数のマーキングが「正しい」ことを完全に認めます。それがデフォルトだったらいいのに。しかし、そうではなく、決勝戦ではコードを理解するのが難しくなります。私が他の誰かのコードにいる場合、私はそれらを引き出すつもりはありませんが、新しいコードを書いている場合、私はそれらを入れません。匿名の内部クラス内から。
166
Alex Miller

忘れないように努力する必要がありますか?

いいえ。Eclipseを使用している場合、保存アクションを設定してこれらのfinal修飾子を自動的に追加できるためです。そうすれば、少ない労力で利益を得ることができます。

44
Peter Hilton

「最終」の開発時の利点は、少なくとも実行時の利点と同じくらい重要です。コードの将来の編集者にあなたの意図について何かを伝えます。

クラスを「最終」とマークすることは、クラスの設計または実装中に、拡張機能を適切に処理する努力をしていないことを示します。読者がクラスに変更を加えることができ、「最終」修飾子を削除したい場合、自分の責任で行うことができます。クラスが拡張機能を適切に処理できるようにするのはユーザー次第です。

変数に「最終」のマークを付ける(およびコンストラクターで変数を割り当てる)ことは、依存性注入に役立ちます。変数の「共同編集者」の性質を示します。

メソッドを「最終」とマークすると、抽象クラスで役立ちます。拡張ポイントの場所を明確に示します。

15
Eric R. Rath

メソッドのパラメーターとローカルをfinalとしてマークすると、問題のメソッドが数ページにわたる不可解な混乱の場合のリファクタリングの補助として役立ちます。 finalをふりかけて、コンパイラ(またはIDE)がスローする「最終変数に割り当てられない」エラーを確認し、「data」と呼ばれる変数が複数(日付)コメントは発生しないことを誓います。

その後、再利用された変数を、使用地点により近い場所で宣言された新しい変数に置き換えることにより、いくつかのエラーを修正できます。次に、メソッド全体を有効範囲の括弧で囲むことができます。突然、「Extract Method」からキーを押すとIDEになり、モンスターがより理解しやすくなります。

もしあなたの方法がnotすでに維持不可能な大破であるなら、人々がそれを前記大破に変えるのを思いとどまらせるために最終的なものを作ることに価値があるかもしれない。しかし、それが短い方法である場合(保守不可ではない)、多くの冗長性を追加する危険があります。特に、Java関数シグニチャーは、引数ごとにさらに6つを追加することなく、80文字に収まるほど十分に困難です!

9
Sam Stokes

常に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 me)、String namefinalはコンパイラエラーを取得します(できないとは言いませんでした)が、式のセマンティクスを破棄するswitchステートメントの後に名前を設定するか、さらにbreakは、finalを使用せずにエラーを発生させることはできません(@Recurseの言うことにもかかわらず)。

    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の例(ラケットまたはチキン):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))
8
Adam Gent

パラメータが誤ってパラメータ値を変更するのを防ぎ、微妙なバグを導入するのに便利です。私はこの推奨事項を無視するために使用しますが、約4時間を費やした後。恐ろしい方法で(何百行ものコードと複数のfor、ネストされたifおよびあらゆる種類の悪い慣行で)私はそれをすることをお勧めします。

 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 ) { ...

 }

もちろん、完璧な世界ではこれは起こらないでしょうが、時には..他のコードをサポートする必要があります。 :(

6
OscarRyz

まあ、これはすべてあなたのスタイルに依存します...変数を変更しないときに決勝を見たい場合は、それを使用します。あなたがそれを見たくない場合は...それを残します。

私は個人的にできるだけ冗長性が好きではないので、実際には必要のない余分なキーワードの使用を避ける傾向があります。

私は動的言語を好むので、冗長性を避けたいのはおそらく驚くことではありません。

したがって、私はあなたが傾いている方向を選んでそれと一緒に行くだけだと思います(どんな場合でも、一貫性を保つようにしてください)。


サイドノートとして、私はそのようなパターンを使用するプロジェクトと使用しないプロジェクトの両方に取り組みましたが、バグやエラーの量に違いは見られませんでした。バグカウントなどを改善しますが、これもスタイルです。修正しないという意図を表現したい場合は、先に進んで使用してください。

6
Mike Stone

たとえば、1年後に誰かがコードを読まなければならないアプリケーションを作成している場合、はい、常に変更するべきではない変数でfinalを使用します。これを行うことにより、コードはより「自己文書化」され、ローカル定数をローカル一時変数として使用するなど、他の開発者が愚かなことをする機会も減ります。

使い捨てのコードを書いている場合は、ああ、すべての定数をわざわざ最終的に決定する必要はありません。

5
Alvin

できる限りfinalを使用します。意図せずにフィールドを変更した場合、そうするとフラグが立てられます。また、Methodパラメーターをfinalに設定します。そうすることで、Java値で渡す。

4
Javamann

変数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変数は実行スコープ内でのみ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) {
  }

このテストクラスの外観を示すために、実際にファイナルであることがわかった場合、ファイナルにマークするとトラブルが軽減されます。一見公開されています。

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

残念ながら、クラスの唯一のコンストラクターはプライベートなので、このクラスを拡張することはできません。 Testクラスの場合、クラスがfinalである必要はありません。 Testクラスは、暗黙的なfinalクラスが問題を引き起こす可能性のある好例です。

したがって、コンストラクターをprivateにしてクラスを暗黙的にfinalにするときに、finalとマークする必要があります。

2
mel3kings

これが明らかであるかどうかは質問から明らかではありませんが、メソッドパラメータを最終的にすることはメソッドの本体にのみ影響します。 [〜#〜] not [〜#〜]は、メソッドの意図に関する興味深い情報を呼び出し側に伝えます。渡されるオブジェクトはメソッド内で変更できます(最終はconstではありません)。変数のスコープはメソッド内にあります。

あなたの正確な質問に答えるために、コードがそれを必要としない限り(インスタンス変数またはメソッドパラメータを含む)、インスタンスまたはローカル変数(変数が内部クラスから参照される)を最終的にするか、またはいくつかの本当に複雑なロジックを明確にすることを気にしません。

インスタンス変数の場合、それらが論理的に定数であれば、それらを最終的にします。

2
ykaganovich

あなたが言及したように、ある程度のトレードオフですが、私は暗黙的な使用よりも明示的な使用を好みます。これは、たとえそれがあなただけであっても、将来のコード管理者のあいまいさを取り除くのに役立ちます。

1
Sean

内部(匿名)クラスがあり、メソッドが包含メソッドの変数にアクセスする必要がある場合、その変数をfinalにする必要があります。

それ以外は、あなたが言ったことは正しいです。

1
anjanb

非常に簡単な答えは、変数を含むFinal、メソッドを含むFinal、クラスを含むFinalの3つのケースがあります。

1.Final with variable:uこの変数を複数回割り当てることはできません。

2.メソッドの最終:このメソッドをオーバーライドすることはできません。

3.最終クラス:最終クラスを拡張することはできません

0
Mohamed Ayed

変数をfinalとして作成する場合は、変数にimmutableキーワードを使用します

変数をfinalとして宣言することにより、開発者が高度にマルチスレッド化された環境で変数の変更の問題を排除するのを支援します。

Java 8リリースでは、「effectively final variable」という概念がもう1つあります。 非最終変数は最終変数として変動します。

ラムダ式から参照されるローカル変数は、最終または実質的に最終でなければなりません

変数は、ローカルブロックでの初期化後に変更されない場合、有効なfinalと見なされます。これは、anonymousクラスまたはラムダ式内でfinalキーワードなしでローカル変数を使用できるようになったことを意味します(それらが効果的にfinalでなければならない場合)。

Java 7まで、匿名クラス内で非最終ローカル変数を使用することはできませんが、Java 8からはできます

これをご覧ください 記事

0
Ravindra babu