クラスにfinal修飾子を付与すると、そのクラスを継承することができなくなる。 (コンパイル時にエラーとなる。)
以下のサンプルプログラムをコンパイルするとエラーとなる。
public final class Sample{
public void test(){
System.out.println("Sample#test");
}
}
Sample2.java
public class Sample2 extends Sample{
public void test(){
System.out.println("Sample2#test");
}
}
コンパイル結果
Sample2.java:2: エラー: final Sampleからは継承できません
public class Sample2 extends Sample{
^
エラー1個
メソッドにfinal修飾子を付与すると、そのメソッドをオーバーライドすることができなくなる。 (コンパイル時にエラーとなる。)
以下のサンプルプログラムをコンパイルするとエラーとなる。
public class Sample{
public final void test(){
System.out.println("Sample#test");
}
}
Sample2.java
public class Sample2 extends Sample{
public void test(){
System.out.println("Sample2#test");
}
}
コンパイル結果
Sample2.java:4: エラー: Sample2のtest()はSampleのtest()をオーバーライドできません
public void test(){
^
オーバーライドされたメソッドはfinalです
エラー1個
変数にfinal修飾子を付与すると、その変数の値を変更することができなくなる。 (コンパイル時にエラーとなる。) 但し、変数がオブジェクトへの参照の場合、参照は変更できないがオブジェクトの値は変更できる。
以下のサンプルプログラムをコンパイルするとエラーとなる。
import java.util.*;
public class Sample3{
// ①インスタンス変数(int型)
private final int maxNum = 10;
// ②定数
private static final int MIN_NUM = 1;
// ③インスタンス変数(Map型)
private static final Map<String, String> map = new HashMap<String, String>();
public void test(){
maxNum = 11; // コンパイルエラー
MIN_NUM = 0; // コンパイルエラー
map = new HashMap<String, String>(); // コンパイルエラー
map.put("key1", "val1"); // コンパイルエラーにはならない
// ④ローカル変数
final String str = "TEST";
str = "TEST2"; // コンパイルエラー
}
}
[解説]
①のインスタンス変数はfinalが付いているので値は変更できない。通常はstaticを付与して定数とするので、このような使い方はあまりしない。
②はstaticとfinalが付いており、かつ参照型ではない。このようなものは定数と呼ばれる。
③はfinalが付いている参照型である。参照自体は変更できないが、オブジェクトの値の変更はできる。結局値を書き換えることができるため、あまりメリットはない。
④ローカル変数にもfinalが付けることができる。あまりメリットがない上、手間が増えたり、可読性が悪くなるため、あまり使用しない方がよい。
Sample3.java:16: エラー: final変数maxNumに値を代入することはできません
maxNum = 11; // コンパイルエラー
^
Sample3.java:17: エラー: final変数MIN_NUMに値を代入することはできません
MIN_NUM = 0; // コンパイルエラー
^
Sample3.java:18: エラー: final変数mapに値を代入することはできません
map = new HashMap(); // コンパイルエラー
^
Sample3.java:32: エラー: final変数strに値を代入することはできません
str = "TEST2"; // コンパイルエラー
^
エラー4個
なおメソッドの引数にもfinalを付与することができる。これもあまりメリットがないため、あまり使用しない。制限は前述の内容と同様である。
import java.util.*;
public class Sample3{
public void test(){
Map map = new HashMap<String, String>();
map.put("key1", "val1");
map.put("key2", "val2");
map.put("key3", "val3");
test(5, map);
}
public void test(final int num, final Map<String, String> map){
num = 15; // コンパイルエラー
map = new HashMap<String, String>(); // コンパイルエラー
map.put("key4", "val4"); // コンパイルエラーにはならない
}
}
コンパイル結果
Sample3.java:36: エラー: finalパラメータnumに値を代入することはできません
num = 15; // コンパイルエラー
^
Sample3.java:37: エラー: finalパラメータmapに値を代入することはできません
map = new HashMap(); // コンパイルエラー
^
エラー2個
finalは主にセキュリティや実装上の縛りを設けることに目的がある。 利用される環境やシステムの特性により、final修飾子を使用するか判断すべきである。
フレームワークや世の中に出回り多数のユーザから使用されるライブラリなどでは使用するメリットはあるが、業務システムで多用すると使い勝手や可読性が悪くなる。 後々「継承やオーバーライドができない」などと面倒な事になりかねない。 またメソッドの引数やローカル変数にfinalを付与することはあまり意味がない。 finalは変数の値を変更されるのを防ぐためであるが、メソッドを作ってる本人が付与しても意味がないのはあきらかである。局所的な話なので、本人が気を付けていればいいだけの話である。 また、どのみち参照型オブジェクトの値は変更できてしまうので、あまりメリットはない。
java標準ライブラリやapache提供ライブラリの開発をしている人にとっては厳密につけることは良いのかもしれないが、少人数でしか使用されないシステムではあまり意味がない。 社内システムのようにもともと環境面においてセキュリティが強固で、かつ少人数でしか開発されない場合、finalを付けるのは定数くらいでよいだろう。 あとは必要に応じて、どうしても譲れない箇所だけ付与するのが得策である。