Java プログラミング言語では、final キーワードは複数のコンテキストで使用され、一度だけ割り当てることができるエンティティを定義します。
final変数に一度代入すると、その変数には常に同じ値が格納されます。変数がオブジェクトへの参照を保持している場合、finalそのオブジェクトの状態はオブジェクトに対する操作によって変更される可能性がありますが、変数は常に同じオブジェクトを参照します (このプロパティは非推移性finalと呼ばれます[1] )。配列もオブジェクトであるため、これは配列にも当てはまります。変数が配列への参照を保持している場合、配列のコンポーネントは配列に対する操作によって変更される可能性がありますが、変数は常に同じ配列を参照します。[2]final
最終授業
finalクラスはjava.lang.Systemサブクラス化できません。サブクラス化によってセキュリティと効率性が向上するため、 やなどの多くの Java 標準ライブラリ クラスは final になっていますjava.lang.String。
例:
パブリック最終クラスMyFinalClass {...}
public class ThisIsWrong extends MyFinalClass {...} // 禁止
最終的な方法
最終メソッドはサブクラスによってオーバーライドまたは非表示にすることはできません。 [3]これは、クラスの機能や一貫性にとって重要なメソッドをサブクラスが変更することで予期しない動作が発生するのを防ぐために使用されます。[4]
例:
パブリッククラスBase {パブリックvoid m1 () {...}パブリック最終void m2 () {...}
パブリック静的void m3 () {...}パブリック静的最終void m4 () {...} }
public class Derived extends Base { public void m1 () {...} // OK、Base#m1() をオーバーライドpublic void m2 () {...} // 禁止
public static void m3 () {...} // OK、Base#m3() を非表示にしますpublic static void m4 () {...} // 禁止}
よくある誤解は、メソッドを として宣言すると、finalコンパイラがメソッドが呼び出される場所に直接挿入できるため効率が向上するというものです (インライン展開を参照)。メソッドは実行時にロードされるため、コンパイラはこれを行うことができません。実行環境とJITコンパイラだけがどのクラスがロードされたかを正確に把握しており、そのため、いつインライン化するか、メソッドが final であるかどうかを判断できるのはそれらだけです。[5]
直接実行可能なプラットフォーム固有のマシン コードを生成するマシン コード コンパイラは例外です。静的リンクを使用する場合、コンパイラは、コンパイル時に計算可能なメソッドと変数がインライン化される可能性があると安全に想定できます。
最終変数
final変数は、初期化子または代入文のいずれかによって 1 回だけ初期化できます。宣言の時点で初期化する必要はありません。これは「blank final」変数と呼ばれます。クラスの blank finalインスタンス変数は、宣言されているクラスのすべてのコンストラクタで確実に代入される必要があります。同様に、blank final 静的変数は、宣言されているクラスの静的初期化子で確実に代入される必要があります。そうでない場合、どちらの場合もコンパイル時エラーが発生します。[6] (注: 変数が参照である場合、これは変数を別のオブジェクトを参照するために再バインドできないことを意味します。ただし、参照先のオブジェクトは、元々可変であった場合は、依然として可変です。)
定数の値とは異なり、final変数の値はコンパイル時に必ずしも判明しているわけではありません。final定数はすべて大文字で表し、単語をアンダースコアで区切るのが良い習慣と考えられています。[7]
例:
パブリッククラスSphere {
// 円周率は普遍定数であり、ほぼ定数と言えます。public static final double PI = 3.141592653589793 ;
public final double radius ; public final double xPos ; public final double yPos ; public final double zPos ;
球( double x 、double y 、double z 、double r ) {半径= r ; xPos = x ; yPos = y ; zPos = z ; }
[ ... ]
}
radius、、、またはをxPos再割り当てしようとすると、コンパイル エラーが発生します。実際、コンストラクターが final 変数を設定しない場合でも、コンストラクターの外部でそれを設定しようとすると、コンパイル エラーが発生します。
yPoszPos
ファイナリティが不変性を保証するわけではないことを説明するために、3 つの位置変数を 1 つに置き換えるとします。
パブリック最終ポジションpos ;
ここで、 は、 、および のpos3 つのプロパティを持つオブジェクトです。には割り当てられませんが、3 つのプロパティは、それ自体が final でない限り、割り当てることができます。
pos.xpos.ypos.zpos
完全な不変性と同様に、final 変数の使用には、特に最適化において大きな利点があります。たとえば、Sphereにはおそらくその体積を返す関数があり、その半径が一定であることがわかれば、計算された体積をメモ化できます。が比較的少なくSphere、その体積が頻繁に必要になる場合は、パフォーマンスが大幅に向上する可能性があります。 の半径を にすると、をSphere final使用するすべてのコードでこの種の最適化が可能であることが開発者とコンパイラに通知されますSphere。
原則に違反しているように見えますがfinal、以下は法的声明です。
for ( final SomeObject obj : someList ) { // obj で何かを行う}
obj変数はループの各反復でスコープ外になるため、実際には各反復で再宣言され、同じトークン(つまりobj)を使用して複数の変数を表すことができます。[8]
ネストされたオブジェクト内の最終変数
最終変数は、不変オブジェクトのツリーを構築するために使用できます。一度構築されると、これらのオブジェクトはそれ以上変更されないことが保証されます。これを実現するには、不変クラスには最終フィールドのみが必要であり、これらの最終フィールド自体には不変型のみを含めることができます。Java のプリミティブ型は不変であり、文字列やその他のいくつかのクラスも同様です。
ツリー内に不変でないオブジェクトが存在することで上記の構成に違反した場合、final 変数を介して到達可能なものは定数であるという期待は成り立ちません。たとえば、次のコードは、原点が常に (0, 0) にある座標系を定義します。原点は though を使用して実装されjava.awt.Point、このクラスはそのフィールドを public かつ変更可能として定義します。つまり、origin以下のサンプル コードが示すように、final 変数のみを含むアクセス パスを介してオブジェクトに到達した場合でも、そのオブジェクトは変更可能です。
java.awt.Pointをインポートします。
パブリッククラスFinalDemo {
静的クラスCoordinateSystem { private final Point origin = new Point ( 0 , 0 );
パブリックポイントgetOrigin (){戻り値origin ; } }
パブリック静的void main ( String [] args ) { CoordinateSystem coordinateSystem = new CoordinateSystem ();
座標系.getOrigin ( ). x = 15 ;
アサートcoordinateSystem.getOrigin () . getX ( ) == 0 ; } }
その理由は、変数を final として宣言することは、この変数が常に同じオブジェクトを指すことを意味するだけだからです。ただし、変数が指すオブジェクトは、その final 変数の影響を受けません。上記の例では、原点の x 座標と y 座標は自由に変更できます。
この望ましくない状況を防ぐための一般的な要件は、不変オブジェクトのすべてのフィールドが final であること、およびこれらのフィールドの型自体が不変であることです。これによりjava.util.Date、java.awt.Pointおよび他のいくつかのクラスは、このような不変オブジェクトで使用することができなくなります。
最終クラスと内部クラス
匿名内部クラスがメソッドの本体内で定義されている場合、finalそのメソッドのスコープ内で宣言されたすべての変数は、内部クラス内からアクセスfinalできます。スカラー値の場合、一度割り当てられた変数の値は変更できません。オブジェクト値の場合、参照は変更できません。これにより、Java コンパイラは実行時に変数の値を「キャプチャ」し、そのコピーを内部クラスのフィールドとして保存できます。外部メソッドが終了し、そのスタック フレームが削除されると、元の変数はなくなりますが、内部クラスのプライベート コピーはクラス独自のメモリ内に残ります。
javax.swing.*をインポートします。
パブリッククラスFooGUI {
public static void main ( String [] args ) { // GUI コンポーネントを初期化しますfinal JFrame jf = new JFrame ( "Hello world!" ); // 内部クラス本体から jf にアクセスできるようにしますjf . add ( new JButton ( "Click me" ));
//パックしてイベントディスパッチスレッドで表示可能にするSwingUtilities.invokeLater ( new Runnable ( ) { @Override public void run ( ) { jf.pack ( ) ; // jfが final でない場合はコンパイル時エラーになるjf.setLocationRelativeTo ( null ) ; jf.setVisible ( true ); } }) ; } }
空白の最終
Java 1.1で導入された空finalは、宣言に初期化子がないfinal変数です。 [9] [10] Java 1.1より前では、final変数には初期化子が必要でした。空finalは、「final」の定義により、1回しか代入できません。つまり、代入が発生すると未代入にする必要があります。これを行うために、Javaコンパイラはフロー分析を実行して、空final変数へのすべての代入に対して、代入前に変数が確実に未代入であることを確認します。そうでない場合は、コンパイル時エラーが発生します。[11]
final boolean hasTwoDigits ; if ( number >= 10 && number < 100 ) { hasTwoDigits = true ; } if ( number > - 100 && number <= - 10 ) { hasTwoDigits = true ; // 最終変数がすでに割り当てられている可能性があるため、コンパイル エラーが発生します。}
さらに、空白の最終語もアクセスされる前に確実に割り当てられる必要がある。[11]
最終的なブール値isEven ;
if (数値% 2 == 0 ) { isEven = true ; }
System . out . println ( isEven ); // else-case で変数が割り当てられなかったため、コンパイル エラーが発生します。
ただし、非最終ローカル変数もアクセスされる前に確実に割り当てられる必要があることに注意してください。[11]
boolean isEven ; // *最終ではない*
if (数値% 2 == 0 ) { isEven = true ; }
System . out . println ( isEven ); // else-case で非 final 変数が割り当てられていないため、同じコンパイル エラーが発生します。
C/C++ の final 変数の類似物
CおよびC++では、類似の構造はconst キーワードfinalです。これはJava とは大幅に異なり、最も基本的な点は型修飾子であることです。つまり、 は型constの一部であり、識別子 (変数) の一部であるだけではありません。これはまた、値の不変性はキャスト (明示的な型変換) によって変更できることを意味し、この場合は「const キャスト」と呼ばれます。ただし、オブジェクトが元々 と宣言されていた場合、const 性をキャストしてオブジェクトを変更すると、未定義の動作が発生します。Java の は厳格な規則であり、final の制限を直接破ったりバイパスしたりするコードをコンパイルすることはできません。ただし、リフレクションを使用すると、final 変数を変更できる場合がよくあります。この機能は、final メンバーを持つオブジェクトを
逆シリアル化するときに主に使用されます。constfinal
さらに、C および C++ はポインタと参照を直接公開するため、ポインタ自体が定数であるかどうかと、ポインタによって指されるデータが定数であるかどうかの間には区別があります。constのようにポインタ自体に適用するとSomeClass * const ptr、参照されるコンテンツは変更できますが、参照自体は変更できません (キャストなし)。この使用法finalにより、Java の変数参照の動作を模倣した動作が発生します。対照的に、 のように参照されるデータのみに const を適用すると、const SomeClass * ptrコンテンツは変更できません (キャストなし) が、参照自体は変更できます。参照と参照されるコンテンツはどちらも として宣言できますconst。
C# の final キーワードの類似物
C# は、言語機能と基本構文の点で Java に似ていると考えられます。Java には JVM があり、C# には .Net Framework があります。Java にはバイトコードがあり、C# には MSIL があります。Java にはポインター (実メモリ) のサポートはありませんが、C# は同じです。
final キーワードに関して、C# には関連するキーワードが 2 つあります。
- メソッドとクラスの同等のキーワードは
sealed - 変数の同等のキーワードは
readonly[12]である。
C/C++ 派生キーワードconstと C# キーワードの主な違いは、はコンパイル時に評価されるreadonlyのに対し、は実行時に評価されるため、後で (実行時に) のみ計算され、修正される式を持つことができる点です。
constreadonly
参照
参考文献
- ^ Coblenz, Michael; Sunshine, Joshua; Aldrich, Jonathan; Myers, Brad; Weber, Sam; Shull, Forrest (2016 年 5 月 14 ~ 22 日)。「不変性に対する言語サポートの検討」。第 38 回国際ソフトウェア工学会議。
- ^ Java 言語仕様 #4.12.4
- ^ 「第8章 クラス」。docs.oracle.com 。2024年4月25日閲覧。
- ^ 「finalクラスとメソッドの記述」。docs.oracle.com 。2024年4月25日閲覧。
- ^ 「Javaの理論と実践:それが最終的な答えでしょうか?」。developer.ibm.com。2009年2月8日時点のオリジナルよりアーカイブ。2024年4月25日閲覧。
- ^ Java言語仕様#8.3.1.2。
- ^ 「Javaプログラミングスタイルガイドライン」。petroware.no 。 2024年4月25日閲覧。
- ^ Pattis, Richard E. 「More Java」。上級プログラミング/実習 15–200 。カーネギーメロン大学コンピュータサイエンス学部。2010年7 月 23 日閲覧。
- ^ Flanagan, David (1997 年 5 月)。「第 5 章 内部クラスとその他の新しい言語機能: 5.6 Java 1.1 のその他の新機能」。Java in a Nutshell (第 2 版)。O'Reilly。ISBN 1-56592-262-X。
- ^ 「第 4 章 型、値、および変数」。Java®言語仕様 (Java SE 8 エディション)。Oracle America, Inc. 2015 年。2015 年2 月 23 日閲覧。
- ^ abc 「確実な代入」。Java®言語仕様 (Java SE 8 エディション)。Oracle America, Inc. 2015 年。2016 年10 月 29 日閲覧。
- ^ Java の final に相当するものは C# で何ですか?
