Java入門|finalの使い方と役割(オーバーライド・継承・値の制御)

自由に広げるだけが設計ではない。
final を使いこなすと、守るべきルール・変えてはいけない値・広げさせない型を、はっきりコードに刻める。

これまで、継承によってクラスを広げたり、オーバーライドによって親クラスのメソッドを子クラス向けに作り直したりできることを見てきました。こうしたしくみは、Java のオブジェクト指向をとても柔軟にしてくれます。ドラゴンボールでたとえるなら、共通の戦士の型を受け継ぎながら、悟空やベジータのように個性を加えていける世界です。

ただし、設計では「自由に変えられること」だけが正解ではありません。
中には、

  • この技の型は絶対に変えさせたくない
  • このクラスはここで完成形なので、これ以上派生させたくない
  • この値は世界のルールとして固定したい

という場面があります。

そんなときに使うのが final です。
final は、単に厳しく縛るための記号ではありません。ここは変えてはいけない という設計者の意図を、Java のコードにはっきり表すための修飾子です。

ドラゴンボールで考えると、final は次のようなものに近いです。

  • 戦士ごとに勝手に変えてはいけない共通の儀式
  • これ以上派生させてはいけない完成済みの戦士の型
  • 世界のルールとして決まっていて、書き換えてはいけない数値

今回は、そんな final の役割を、オーバーライド・継承・値の制御という 3 つの視点から整理していきます。
同じ final でも、どこに付けるかによって意味が変わるので、その違いを丁寧に見ていきましょう。

final は「それ以上変更させない」を表す

final の基本イメージは、とてもシンプルです。
それ以上変更させない。これが final の中心にある意味です。

ただし、どこに付けるかで、何を変更させないのかが変わります。

final を付ける場所変更できなくなるもの意味
メソッドオーバーライド子クラスで上書きできない
クラス継承サブクラスを作れない
フィールド値の再代入値を変更できない

この表が、final を理解する出発点です。

ドラゴンボール風に言うと、

  • メソッド final は 技の型を固定する
  • クラス final は 戦士の型そのものを固定する
  • フィールド final は 世界の決まりの数値を固定する

という違いになります。

メソッドに final を付ける意味

まずは、メソッドに付ける final です。

メソッドに final を付けると、そのメソッドはサブクラスでオーバーライドできなくなります。
つまり、親クラスが持っているそのメソッドの形や役割を、子クラス側で作り直せなくなります。

たとえばスーパークラス Saiyan に show() があるとして、それを

public final void show()

のように定義すると、サブクラス EliteSaiyan で同じ show() を定義して上書きすることはできません。

なぜメソッドをオーバーライド禁止にするのか

オーバーライドは便利です。
でも、便利だからこそ、何でも自由に上書きできるようにしてしまうと困ることがあります。

たとえば、親クラスにあるメソッドが

  • 共通ルールとして守らせたい処理
  • 順番や形式が重要な処理
  • 安全のために固定すべき処理

だった場合、子クラスで勝手に変えられると設計の意味が崩れてしまいます。

ドラゴンボールでたとえるなら、全戦士共通の「認証の儀式」のようなものです。

  • 名前を名乗る
  • 所属を示す
  • 身元を確認する

という流れが絶対のルールなら、それを悟空型やベジータ型のクラスが勝手に変えてはいけません。
そのとき show() のようなメソッドに final を付けておけば、上書きされずに済みます。

つまりメソッド final は、
この振る舞いは共通ルールとして固定する
という意思表示です。

メソッド final の見方を整理する

メソッドに final を付ける場面を、考え方として整理するとこうなります。

こんなときfinal を付ける意味
全クラスで同じ動きを守らせたいオーバーライド禁止にする
子クラスに自由に変えてほしくない親クラスの処理を固定する
処理の順番や意味が崩れると困る共通ルールを保護する

オーバーライドは子クラスの個性を出すしくみでした。
それに対してメソッド final は、個性を出してはいけない場所を決める ためのものです。

クラスに final を付ける意味

次に、クラスに付ける final を見ていきます。

クラスの先頭に final を付けると、そのクラスは継承できなくなります。
つまり、そのクラスを土台にして新しいサブクラスを作ることができません。

たとえば、

final class Saiyan
{
    ...
}

のように書くと、

class EliteSaiyan extends Saiyan

のような宣言はできなくなります。

なぜクラスを継承禁止にするのか

継承は強力です。
でも、クラスによっては「これ以上広げないほうがよい」ものがあります。

たとえば、

  • そのクラスが設計として完成している
  • 内部ルールが厳密で、派生されると困る
  • 想定外の使い方を防ぎたい

という場合です。

ドラゴンボールでたとえるなら、「この戦士の型はここで完成していて、これ以上進化系の派生を許さない」と決める感じです。

もし何でも継承できるようにしておくと、設計者が想定していない戦士型がどんどん増えてしまうかもしれません。
その結果、元のクラスが持っていたルールや意味が崩れる可能性があります。

だから final class は、
この型はここで完成です
と宣言するための方法でもあります。

クラス final の役割を整理する

こんなときfinal を付ける意味
そのクラスを完成形として扱いたい継承を禁止する
派生されると設計が崩れるサブクラス作成を防ぐ
使い方を厳密に固定したい型の広がりを止める

継承は拡張性を生みます。
一方でクラス final は、拡張性よりも安定性を優先する ときの選択です。

フィールドに final を付ける意味

最後に、フィールドに付ける final です。

フィールドに final を付けると、その値は変更できなくなります。
これは「途中で書き換えてはいけない値」を表すときに使います。

たとえば、

static final int MAX_DRAGON_BALL = 7;

のように書けば、この値は固定になります。

フィールド final はなぜ大切か

プログラムの中には、変わってよい値と、変わってはいけない値があります。

ドラゴンボールで考えると、

  • 現在の戦闘力は修行や変身で変わることがある
  • でもドラゴンボールの総数のような世界の決まりは変わってはいけない

という違いがあります。

もし、世界の決まりとなる数値が途中で変更できてしまうと、

  • 前提が崩れる
  • 処理結果が不安定になる
  • バグの原因になる

といった問題が起こります。

だから final を付けておくことで、
この値は固定です
とコードの中ではっきり示せます。

フィールド final は定数につながる

フィールドに付けた final は、決まった値を表す「定数」としてよく使われます。

特に static final の形になることが多いのは、

  • static でクラス全体に共通の値にする
  • final で変更不可にする

という 2 つの役割が重なるからです。

ドラゴンボール風に言うなら、特定の戦士だけの事情ではなく、世界やルール全体で共通して使う決まりの数です。

たとえば、

  • DragonBallRule.MAX_COUNT
  • SaiyanRule.DEFAULT_TAIL_COUNT

のような形で考えるとイメージしやすいです。

そしてこうした定数は、宣言したときに必ず初期化しておく必要があります。
なぜなら、あとから値を変えられないからです。

final とオーバーライドの関係

この話で特に重要なのは、final とオーバーライドの関係です。

オーバーライドは、子クラスが親クラスのメソッドを自分流に作り直すための大切なしくみでした。
でも、すべてのメソッドを自由にオーバーライドできるようにすると、共通ルールが壊れる可能性があります。

そこで設計では、次のような使い分けをします。

メソッドの種類どうするか
子クラスごとに変えてよいもの通常どおりオーバーライド可能にする
親クラスのルールとして固定したいものfinal を付ける

ドラゴンボールで考えるなら、

  • 必殺技の見せ方は戦士ごとに変えてよい
  • でも戦士認証や絶対ルールの表示は変えさせない

という使い分けです。

つまり final は、オーバーライドを否定するのではなく、オーバーライドを許す場所と許さない場所を分けるための道具 です。

final と継承の関係

クラス final は、継承を禁止します。
これは、継承が悪いからではありません。継承させないこと自体が、設計として正しい場合があるからです。

継承を許すと、

  • クラスが増える
  • 派生の自由度が上がる
  • オーバーライドの幅が広がる

という利点があります。

その一方で、

  • 想定外のサブクラスが作られる
  • 元のルールが崩れる
  • 保ちたかった整合性が失われる

こともあります。

final class は、この広がりを意図的に止める仕組みです。
つまり、この型はここまで という境界線を引いているわけです。

final と値の制御の関係

フィールド final は、値を通して設計を安定させます。

オブジェクト指向では、フィールドの値が状態を表します。
その中には、変わるべき値と、変わってはいけない値があります。

ドラゴンボールで言えば、

  • 戦士の現在戦闘力は状況で変わる
  • でも神龍を呼ぶためのドラゴンボールの必要数は変わらない

という違いです。

この「変えてよいもの」と「変えてはいけないもの」をコードの中ではっきり分けると、設計はかなり読みやすくなります。

つまりフィールド final は、
この値は状態ではなくルールです
とプログラムに伝えるための表現でもあります。

final をどう読み取ればよいか

final を見たときは、「何が止められているか」を考えると理解しやすくなります。

final の対象止めているもの読み取り方
メソッドオーバーライドこの振る舞いは固定
クラス継承この型は完成形
フィールド値の変更この数値は固定

この見方を持っておくと、コードの中で final が出てきたときに、
「これは何を守ろうとしているのか」
をすぐに読み取りやすくなります。

Math クラスのような final の活用イメージ

final を使った代表的な考え方として、計算に関わるクラスを思い浮かべると整理しやすいです。
たとえば数学関係の計算を行うクラスでは、

  • 勝手に継承されて振る舞いを変えられたくない
  • 円周率のような値は定数として固定したい

という事情があります。
こうした場面で final はとても自然です。

ドラゴンボール風に言えば、世界の法則や宇宙共通の計算ルールのようなもので、個々の戦士が勝手に書き換えてよいものではありません。

このように final は、柔軟性を減らすためではなく、変えてはいけないものを明確に守るため に使われます。

final をドラゴンボールでひとつの感覚にまとめる

最後に、ドラゴンボールの感覚でまとめてみましょう。

final は、戦士の世界にある「絶対に破ってはいけない決まり」をコードにしたものです。

  • メソッド final は この技の型は変えるな
  • クラス final は この戦士の型はこれ以上派生させるな
  • フィールド final は この数値は世界の決まりだから変えるな

という意味になります。

オブジェクト指向では、自由に広げられることは強みです。
でも、本当に強い設計は、自由なところだけでなく、守るべきところも明確です。

その「ここは固定する」という線を引くのが final です。

図で final の役割を整理する

final は 3 つの使い方があるので、図にすると全体像がつかみやすくなります。

左側では、メソッドに final を付けることで、子クラスがその技を勝手に上書きできないことを見ます。

中央では、クラスに final を付けることで、その型から新しい戦士クラスを派生できないことを見ます。

右側では、フィールドに final を付けることで、値が固定されて変更できないことを見ます。

そして下部の final = それ以上変更させない が、3 つに共通する意味です。
この図を頭に入れておくと、final は 1 つの単語でも、設計のどこを固定しているのかを見分けやすくなります。