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

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

質問

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

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

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

役に立ちましたか?

解決

没頭:

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

検討するが、慎重に使用する:

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

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

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

他のヒント

  

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

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

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

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

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

メソッドのマーク" final&quot ;;抽象クラスで役立ちます。拡張ポイントの場所を明確に示します。

メソッドのパラメーターとローカルを final としてマークすることは、問題のメソッドが数ページにわたる理解できない混乱である場合のリファクタリングの補助として役立つことがわかりました。 final をふりかけ、「最終変数に割り当てられないもの」を確認します。コンパイラ(またはIDE)がスローするエラー。変数" data"が呼び出される理由を発見するかもしれません。いくつかの(期限切れの)コメントが発生しないと誓っていても、最終的にはnullになります。

その後、再利用された変数を、使用地点により近い場所で宣言された新しい変数に置き換えることにより、いくつかのエラーを修正できます。次に、メソッドの全体をスコーピングで囲むことができ、突然「メソッドの抽出」から1つのIDEキーを押すことになります。あなたのモンスターはよりわかりやすくなりました。

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

常に 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();
    }

Now別の case ステートメントを追加し、 name を設定しない場合、コンパイラーは失敗します。すべてのケース(変数を設定した場合)で中断しないと、コンパイラーも失敗します。これにより、JavaをLispの let 式と非常に類似させ、コードが大量にインデントされないようにします(字句スコープ変数のため)。

そして、@ Recurseが述べたように(ただし、明らかに-1 me)、 String name final を作成せずに上記を実行して、コンパイラエラーを取得できますできませんでしたが、式のセマンティクスを破棄するswitchステートメントの後に名前を設定するか、エラーを引き起こすことができない 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の例(ラケットまたはチキン):

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

まあ、これはすべてあなたのスタイルに依存します...変数を変更しないときにファイナルを見たい場合は、それを使用します。見たくない場合は、それを省いてください。

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

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

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


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

パラメータの値が誤って変更されるのを防ぎ、微妙なバグを導入するのに便利です。私はこの推奨事項を無視するために使用しますが、約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 ) { ...

 }

もちろん、完璧な世界ではこれは起こりませんが、他のコードをサポートしなければならないこともあります。 :(

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

スローアウェイコードを書いている場合は、いや、すべての定数を特定して最終的なものにすることを気にしないでください。

できる限りfinalを使用します。そうすることで、フィールドを意図せずに変更した場合にフラグが立てられます。また、Methodパラメーターをfinalに設定します。そうすることで、Javaの値渡しを忘れてパラメーターを「設定」しようとしたときに引き継いだコードからいくつかのバグを見つけました。

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

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

インスタンス変数については、論理的に定数である場合は最終変数にします。

変数 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

最終クラスおよび最終メソッドは、それぞれ拡張または上書きできません。

編集:カプセル化に関する最終的なクラスの問題に対処するには:

クラスをfinalにする方法は2つあります。最初は、クラス宣言でキーワードfinalを使用することです。

public final class SomeClass {
  //  . . . Class contents
}

クラスをfinalにする2番目の方法は、すべてのコンストラクターをprivateとして宣言することです:

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 . . . 
  }
}

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

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

おっしゃるとおり、多少のトレードオフがありますが、暗黙的な使用よりも明示的な使用をお勧めします。これは、将来のコード管理者の曖昧さを取り除くのに役立ちます-それがあなただけであっても。

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

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

変数を immutable

にする場合、変数に final キーワードを使用します

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

Java 8リリースでは、&quot; 実質的に最終的な変数 &quot;という概念がもう1つあります。 非最終変数は最終変数として振る舞うことができます。

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

  

ローカルブロックでの初期化後に変更されない場合、変数は 有効な最終 と見なされます。これは、事実上finalでなければならない限り、匿名クラスまたはラムダ式内でfinalキーワードなしでローカル変数を使用できることを意味します。

Java 7までは、匿名クラス内で最終でないローカル変数を使用できませんが、Java 8では

この記事

非常に簡単な答えは、変数があるFinal、メソッドがある&amp;&amp;の3つのケースです。クラスでの最終..

1。変数の最終:uこの変数を複数回割り当てることはできません。

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

3.Final with classes:uは最終クラスを拡張できません

まず、finalキーワードを使用して変数を定数にします。定数は、変化しないことを意味します。例:

final int CM_PER_INCH = 2.54;

センチメートル/インチは変わらないため、変数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 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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top