Java の継承の制約を完全解説!final クラスや final メソッドの扱いを初心者向けにやさしく解説
新人
「Javaでクラスを継承できないって聞いたんですけど、本当ですか?」
先輩
「うん、本当だよ。finalクラスっていう特別なクラスは、継承できないように作られているんだ。」
新人
「じゃあ、どういうときにfinalクラスを使うんですか?」
先輩
「よし、今日はJavaの継承の制約について、finalクラスやfinalメソッドの基本を一緒に見ていこう!」
1. Javaの継承ってなに?(基本の復習)
Javaの継承とは、すでにあるクラス(これを親クラスといいます)の機能を引き継いで、新しいクラス(子クラス)を作る仕組みのことです。
たとえば、動物を表すAnimalという親クラスを作って、その子クラスとしてDogやCatを作れば、Animalに書いた共通の処理をDogやCatでも使えます。
public class Animal {
void speak() {
System.out.println("鳴きます");
}
}
public class Dog extends Animal {
void bark() {
System.out.println("ワンワン!");
}
}
このように、子クラスは親クラスのspeak()メソッドも使えるようになります。これがJavaの継承の基本的な考え方です。
2. 継承できないクラスとは?(finalクラスの意味)
Javaでは、特定のクラスを継承できないようにする方法があります。それがfinalを使ったクラスです。finalクラスとは、「これ以上子クラスを作らせたくない」ときに使います。
finalをつけたクラスは、そのクラスをもとにした子クラスを作ろうとするとエラーになります。
public final class Calculator {
public void add(int a, int b) {
System.out.println(a + b);
}
}
// 下のように継承しようとするとエラーになる
// public class AdvancedCalculator extends Calculator {
// // エラー!Calculatorはfinalなので継承できない
// }
このように、finalクラスは継承の制約がある特別なクラスです。
なぜこんな制約があるのかというと、そのクラスがとても大事で、勝手に動きを変えてほしくない場合に使うからです。
たとえば、安全性が大事なクラスや、ライブラリの内部処理などでは、finalを使って設計されることがあります。
3. finalメソッドとは?(上書きできないメソッド)
Javaでは、メソッドにもfinalをつけることができます。finalメソッドとは、子クラスで上書き(オーバーライド)できないメソッドのことです。
たとえば、親クラスで定めた重要な処理を、子クラスで勝手に変えてほしくないときに使います。
public class Animal {
public final void eat() {
System.out.println("ごはんを食べます。");
}
}
public class Dog extends Animal {
// public void eat() { // これはエラーになります!
// System.out.println("ドッグフードを食べます。");
// }
}
このように、finalメソッドを上書きしようとすると、Javaがエラーを出して教えてくれます。
4. finalクラス・finalメソッドの使いどころ
Javaのfinalキーワードは、クラスにもメソッドにも使えます。そして、それぞれ使いどころがあります。
finalクラスは、「これ以上変更されたくないクラス」に使います。たとえば、セキュリティに関わる処理や、ライブラリなどの安全な動きを守るときに便利です。
finalメソッドは、「子クラスで動きを変えてはいけないメソッド」に使います。たとえば、ログを出力する処理や、正確に動かす必要がある計算などです。
実際にJavaの標準クラスでも、StringクラスやMathクラスなどには、finalが使われていることがあります。これは、動きが固定されていて、変更してはいけないことを示しているのです。
5. オーバーライドとの違いを確認しよう
Javaのfinalとオーバーライドは、正反対の考え方を持っています。オーバーライドは「親クラスの動きを子クラスで変えること」、finalは「親クラスの動きを変えられないようにすること」です。
たとえば、オーバーライドできると、子クラスで自由に書き換えられますが、finalを使うとそれができなくなります。
public class Parent {
public void greet() {
System.out.println("こんにちは、親クラスです。");
}
}
public class Child extends Parent {
@Override
public void greet() {
System.out.println("こんにちは、子クラスです。");
}
}
こんにちは、子クラスです。
これはオーバーライドの例です。親のメソッドを子が書き換えて、新しい動きをさせています。
一方、finalをつけるとこうなります。
public class Parent {
public final void greet() {
System.out.println("こんにちは、親クラスです。");
}
}
public class Child extends Parent {
// @Override
// public void greet() { // エラーになります!
// System.out.println("こんにちは、子クラスです。");
// }
}
このように、finalをつけることで、オーバーライドを禁止できるのです。
Javaでは、クラスやメソッドが増えてくると、うっかり動きを変えてしまうことがあります。そういったミスを防ぐためにも、finalの使い方を覚えておくと安心です。
6. finalの使い方でよくある間違い
Javaのfinalキーワードは便利ですが、間違って使うとエラーになったり、意図しない動きになることがあります。
まず、finalをつけたあとでそのクラスやメソッドを変更しようとするとエラーになります。たとえば、finalメソッドを上書きしたり、finalクラスを継承しようとするのはNGです。
public final class Vehicle {
public void run() {
System.out.println("走行中");
}
}
// エラーになる継承
// public class Car extends Vehicle {
// }
このようにfinalの意味を知らずに継承しようとすると、Javaのルールに反するため、Pleiadesのエラー表示で気づくことになります。
また、finalメソッドをうっかりオーバーライドしようとすることもよくあります。
public class Animal {
public final void sleep() {
System.out.println("眠ります。");
}
}
// public class Cat extends Animal {
// public void sleep() { // エラーになります!
// System.out.println("ネコが寝ています。");
// }
// }
エラーの原因がfinalによる制約だったということを知っておくと、こうしたミスも防ぎやすくなります。
7. 初心者がfinalを正しく使うコツ
初心者のうちはfinalの使いどころが分からず、なんとなく使わないということも多いですが、実はとても役立つキーワードです。
どのタイミングで使うと良いかのポイントは次の通りです。
- クラス:継承されたくないときにfinalをつける
- メソッド:動きを固定したいときにfinalをつける
たとえば、学校の「出席番号」を自動で表示するクラスを作ったとします。これはfinalで固定しておくと、誰かに勝手に継承されて書き換えられる心配がありません。
public final class Attendance {
public void printNumber() {
System.out.println("出席番号を表示します。");
}
}
あとから変更されないようにしておくことは、プログラムの安全性を高める上でとても大切な考え方です。
8. 継承とfinalを使った簡単な設計例
Javaのfinalと継承を組み合わせることで、わかりやすく、安全なクラス設計ができます。ここでは、図書館のシステムを例に考えてみましょう。
まず、LibraryItemという親クラスを作り、本や雑誌などを表す子クラスを作ります。そして、LibraryItemにfinalメソッドを使って、絶対に変えてはいけない処理(たとえば、共通のIDを表示する処理)を固定します。
public class LibraryItem {
public final void showID() {
System.out.println("共通のIDを表示します。");
}
public void describe() {
System.out.println("図書館の資料です。");
}
}
public class Book extends LibraryItem {
@Override
public void describe() {
System.out.println("これは本です。");
}
}
public class Magazine extends LibraryItem {
@Override
public void describe() {
System.out.println("これは雑誌です。");
}
}
このようにしておけば、IDの表示方法は固定しつつ、それぞれの資料の説明だけを変えることができます。
Javaのfinalと継承の仕組みをうまく組み合わせることで、安全で管理しやすいプログラムが書けるようになります。