この節では予約語finalについて解説します。
finalは修飾子です。いろいろな場所で使われますが、使われる場所によって意味合いが異なります。ということで、以下 6種類に場合分けして解説していきます。
参照型のフィールド、引数、ローカル変数についても解説します。
あるクラスを finalとして宣言したとき、そのクラスを継承したクラスを作ることはできません。例えば、以下の場合、
public final class A { }
以下のコードは、クラスを継承しようとしているためビルドエラーになります。
public class B extends A { // ビルドエラー }
Javaに用意されているクラスの中では、例えば、Stringクラスや Systemクラス、Mathクラスなどが finalなクラスとして宣言されています。Java SE 7(Java 1.7)の Stringクラスと Systemクラス、Mathクラスの説明書(API)を改めて掲示しておきます→Stringクラス・Systemクラス・Mathクラス。
例えば、Stringクラスの場合、以下の記述があることを確認できるでしょうか。
public final class String extends Object implements Serializable, Comparable<String>, CharSequence
あるメソッドを finalとして宣言したとき、そのクラスを継承したクラスにおいて、そのメソッドをオーバーライドすることはできません。例えば、以下の場合、
public class A { public final void method() { ; } }
以下のコードは、メソッドをオーバーライドしようとしているためビルドエラーになります。
public class B extends A { @Override public void method() { // ビルドエラー ; } }
コンストラクタの宣言に final修飾子を使用することはありません。
基本型フィールドに final修飾子が付加された場合、そのフィールドの値は変えられません。従って、以下のような記述はビルドエラーになります。
public class A { private static final int MAX_VALUE = 100; public void method() { System.out.println("値は" + MAX_VALUE); MAX_VALUE = 200; // ビルドエラー } }
上記の例について、本ウェブサイトの記述指針に従うならば、6行目と 8行目についてA.MAX_VALUE
と記述すべきなのですが、とても違和感があるため、MAX_VALUE
と記述しています。
値が変えられない以上、もはや変数ではないので、定数と呼ばれます。Java言語では、定数は、一般的に大文字で表記するルールになっています。
finalな基本型フィールドの場合、予約語static
を合わせて使用する場合がとても多いです。「定数」なのですから、基本的にはすべてのインスタンスで同じ値となるため、staticなフィールドにしたほうがメモリの節約になります。
例えば、Mathクラスの staticなフィールド PIは final修飾子が付加されています。Java SE 7(Java 1.7)の Mathクラスの説明書(API)を改めて掲示しておきます→Mathクラス。この中に、以下の記述があることを確認できるでしょうか。
PI public static final double PI 円周とその直径の比 pi にもっとも近い double 値です。
基本型引数に final修飾子が付加された場合、その引数の値は変えられません。従って、以下のような記述はビルドエラーになります。
public class A { public void method(final int x, int y) { x = 200; // ビルドエラー } }
上記の例について、変数 xには final修飾子が付加されているため値を変更することができません。なお、以前、「値渡しと参照渡し」で解説したように、仮に引数の値を変えたとしても、基本型の場合はメソッドを呼び出した側の値が変更されることはありません。例えば、今回、変数 yの値を変えたとしても、呼び出し元の引数に設定した変数の値が変わる訳ではありません。
なお、基本型フィールドや基本型ローカル変数の場合、変数というより定数ということで、Javaでは大文字で記述しますが、引数の場合は、呼ばれるたびに格納される値が異なることや、Javadocコメントの兼ね合いもあって、大文字ではなく通常通り記述します。
基本型ローカル変数に final修飾子が付加された場合、そのローカル変数の値は変えられません。従って、以下のような記述はビルドエラーになります。
public class A { public void method() { final int MAX_VALUE = 200; System.out.println("値は" + MAX_VALUE); MAX_VALUE = 200; // ビルドエラー } }
値が変えられない以上、もはや変数ではないので、定数と呼ばれます。Java言語では、定数は、一般的に大文字で表記するルールになっています。
参照型変数と finalを組み合わせて使うことはほとんど無いと思います。参照型変数に対する finalは、参照型変数の参照の差し替えを禁止するためのものです。参照先インスタンスの内容は書き換えることができます。このような性質から、定数とは言いがたいです。変数名も、大文字ではなく通常通りの記述になると思いますが、いかんせん見かけることが少なく、間違っているかもしれません。
以下、フィールド、引数、ローカル変数について見ていきます。
参照型フィールドに final修飾子が付加された場合、そのフィールドの参照先は変更できません。従って、以下のような記述はビルドエラーになります。
public class A { private final Point point = new Point(10, 20); public void method() { this.point = null; // ビルドエラー this.point = new Point(10, 20); // ビルドエラー this.point.x = 500; // OK this.point.translate(150, 250); // OK } }
6行目、7行目のように、変数 pointの参照先を変更しようとするとビルドエラーになります。
ただし、9行目、10行目のように、Point型インスタンスの内容を書き換えることができます。finalは参照型変数の参照先を制限するだけです。
参照型引数に final修飾子が付加された場合、その引数の参照先は変更できません。従って、以下のような記述はビルドエラーになります。
public class A { public void method(final Point point) { point = null; // ビルドエラー point = new Point(10, 20); // ビルドエラー point.x = 500; // OK point.translate(150, 250); // OK } }
4行目、5行目のように、変数 pointの参照先を変更しようとするとビルドエラーになります。
ただし、7行目、8行目のように、Point型インスタンスの内容を書き換えることができます。finalは参照型変数の参照先を制限するだけです。
参照型ローカル変数に final修飾子が付加された場合、そのローカル変数の参照先は変更できません。従って、以下のような記述はビルドエラーになります。
public class A { public void method() { final Point point = new Point(10, 20); point = null; // ビルドエラー point = new Point(10, 20); // ビルドエラー point.x = 500; // OK point.translate(150, 250); // OK } }
6行目、7行目のように、変数 pointの参照先を変更しようとするとビルドエラーになります。
ただし、9行目、10行目のように、Point型インスタンスの内容を書き換えることができます。finalは参照型変数の参照先を制限するだけです。
参照型変数に対する finalは、あくまでも参照型変数の参照の差し替えを阻止するためのものでした。言い換えれば、final修飾子によって、インスタンスの内容を変更することを阻止することはできない、ということになります。