Javaの「オーバーライド」をざっくりまとめてみた[Java入門]



## はじめに

こんにちは。
プログラミング初心者Wakinozaと申します。
Java勉強中に調べたことを記事にまとめています。

十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。
間違い等あれば、指摘いただけると助かります。

## 対象読者

- Javaを勉強中の方
- Java Silver試験を勉強中の方
- Javaのオーバーライドをざっくり知りたい方

## 目次

1\. オーバーライドとは
2\. Overrideアノテーション
3\. super
4\. final
5\. フィールドの隠蔽

## 本文

### 1\. オーバーライドとは

オーバーライドとは、継承したメソッドを再定義することです。

スーパークラスのメソッドは、アクセス修飾子が許せばサブクラスに継承され、サブクラスで利用することができます。しかし時に、継承したメソッドと同じ名前・目的で、処理内容を少しだけ変更したい場合があります。そのような時の用いるのが、オーバーライドです。

オーバーライドする場合は、スーパークラスのメソッドと同じシグネチャ（メソッド名と引数の数・型・順）で、サブクラスにメソッドを再定義します。
以下のコードをご覧ください。

```Java
public class Super {
  public void greet(){
    System.out.println("Good Morning!");
  }
}

public class Sub extends Super {
  public void greet(){
    System.out.println("Hello!");
  }
}
```

Superクラスで定義されているgreet()メソッドを、Subクラスでオーバーライドしています。このとき、Subクラスのインスタンスから greet() メソッドを呼び出すと、Subクラスでオーバーライドされた greet() が実行され、画面には"Hello!"と表示されます。

一見すると、スーパークラスのメソッドをサブクラスで「上書き」しているようにも見えます。
しかし実際は、サブクラスのインスタンス内にスーパークラスのメソッドも、サブクラスのメソッドも両方存在しています。インスタンス内に同じシグネチャのメソッドが複数ある場合、サブクラスで定義したメソッドを優先的に呼び出す仕組みとなっているため、オーバーライドが可能になっているのです。

オーバーライドには、いくつかの条件があります。
サブクラスのメソッドは、スーパークラスのメソッドに対して以下の５つの条件を全て満たす必要があります。

- メソッド名が同じであること
- メソッドの引数の数とデータ型と順が同じであること。（引数名は異なっているのは、問題ない）
- アクセス修飾子が一致しているか、スーパークラスより制限が緩いアクセス修飾子であること
- 戻り値のデータ型が同じであるか、そのかたの「下位方向の型」であること
- 例外処理のthrowsに指定可能なのは、スーパークラスのthrowsに指定された例外クラスと同じか、それより「下位方向の型」であること

:::note info
「下位方向の型」とは、継承であればサブクラス側の型、インターフェースであれば実装クラス側の型のことです
:::

### 2\. Overrideアノテーション

オーバーライドでは、スーパークラスのメソッドのシグネチャと完全に一致していなければなりません。
ところが人間は、時にミスをします。オーバーライドしたつもりでも、シグネチャを１字でも書き間違えれば、サブクラスで独自に定義したメソッドと扱われ、オーバーライドとはみなされません。厄介なことに、文法に問題がないため、このミスはコンパイラでもチェックできません。ミスの発見が遅れ、実行時に予期せぬエラーを引き起こす可能性があります。

```Java
public class A {
  public void doA(){
    //any code
  }
}

public class B extends A {
  public void deA(){} 
  //doA()メソッドをオーバーライドしてつもりだが、
  // 書き間違いのためオーバーライドにならない。
  // コンパイラでチェックできないため、
  // 実行時までミスがわからない。
}
```

このようなミスを防ぐための仕組みが、「Overrideアノテーション」です。
「アノテーション」とは、プログラムの処理に影響を与えることなく、コードにメタデータ（付加情報）を付与するための機能です。コードにメタ情報を記述することで、不正なコードをコンパイラ時にチェックしたり、実行時の処理を変更したり、ドキュメントを生成したり、さまざまな処理を行うことができます。

「Overrideアノテーション」は、「記述したメソッドがオーバーライドしている」ということを明示するアノテーションです。
Overrideアノテーションを付加していれば、正しくオーバーライドできているかをコンパイラがチェックしてくれます。書き間違いでオーバーライドできていない場合はコンパイラエラーとなるため、早期にミスを発見できます。

Overrideアノテーションを使う場合は、オーバーライドしたサブクラス側のメソッドに「@Override」を記述します。

```Java
public class A {
  public void doA(){
    //any code
  }
}

public class B extends A {
  @Override
  public void deA(){} 
  //書き間違いのためdoA()メソッドのオーバーライドにはならない。
  //アノテーションでメタ情報を付加したので、
  //書き間違いをコンパイラがチェックしてくれる。
}
```

オーバーライドを意図したメソッドを宣言する場合は、「Overrideアノテーション」も記述した方が良いでしょう。

### 3\. super

「super」キーワードは、自クラスが継承しているスーパークラスを表現するのに用いられます。
具体的には２つの用途で用いられます。

1,スーパークラスのメソッドを呼び出す
通常の継承関係では、サブクラスからスーパークラスのメソッドをそのまま呼び出せました。しかし、オーバーライドすると、サブクラスからスーパークラスのメソッドは呼び出せなくなります。
オーバーライドしているメソッドの元にあたる、スーパークラスで定義されたメソッドを特別に呼び出したい場合に用いるのが、superキーワードです。
具体的には、サブクラス側に、super.メソッド名()と記述することで、オーバーライドする前のスーパークラスで定義されたメソッドを呼び出すことができます。

2,スーパークラスのコンストラクタを呼び出す
クラスを継承しても、コンストラクタは継承されません。
そのため、スーパークラスから継承したフィールド値に初期値を代入したい場合は、superキーワードを使います。
具体的には、サブクラスのコンストラクタの１行目にsuper(初期値)と記述することで、スーパークラスのコンストラクタが呼び出し、フィールドに初期値を渡すことができます。コンストラクタがオーバーロードしている場合は、引数の数や型が合うものが呼び出されます。
先ほど、コンストラクタは継承されないと言いました。
しかし内部では、サブクラスのインスタンスを生成した際に、必ずスーパークラスのコンストラクタが実行されてから、サブクラスのコンストラクタが実行される仕組みとなっています。
そのため、サブクラスのコントラクタにsuperが記述されていない場合は、引数なしの「super()」が自動的に追加されます。スーパークラスに引数なしのコンストラクタがあれば問題ないのですが、引数なしのコンストラクタがない場合はコンパイラエラーとなります。

### 4\. final

変数に再代入したくない時は、「final」キーワードをつけて、定数として宣言します。

この「final」キーワードは、継承時にも使えます。
もし、特定のクラスやメソッドを継承したくない場合は、宣言時に「final」キーワードを記述します。

```Java
アクセス修飾子 final class クラス名(引数){}

アクセス修飾子 final 戻り値の型 メソッド名(引数){}
```

メソッド宣言時に「final」キーワードを記述すると、サブクラス側でそのメソッドをオーバーライドできなくなります。
また、クラス宣言時に「final」キーワードを記述すると、そのクラスを継承することができなくなります。

### 5\. フィールドの隠蔽

「オーバーライド」はメソッドに関する動作です。
フィールドには、オーバーライドという概念は存在しません。
もしサブクラスで、スーパークラスと同じ変数名のフィールドを定義すると、スーパークラスのフィールドは「隠蔽」されます。
「フィールドの隠蔽」は「オーバーライド」とは似ているように思えますが、挙動が異なるため、注意が必要です。

具体的に何が違うのでしょうか。
インスタンス生成時、参照変数の型とインスタンスの型が違う場合を考えてみましょう。
オーバーライドでは、どのメソッドが呼び出されるかはインスタンスの型によって決まります。一方、フィールドが隠蔽されている場合は、どのフィールドが呼び出されるかは参照変数の型によって決まります。

このように、オーバーライドとフィールドの隠蔽では、呼び出されるメンバが異なります。
この挙動の違いは、コードの可読性を落とし、思わぬエラーの原因ともなります。
そのため、継承関係のあるクラスでは、フィールドの隠蔽を避けるのが無難です。

## まとめ

- オーバーライドとは、継承したメソッドを再定義すること
- 「Overrideアノテーション」で、オーバーライドのミスをコンパイラにチェックさせることができる
- 「super」キーワードは、スーパークラスのメソッドやコンストラクタを呼び出す際に用いる
- 「final」キーワードで、クラスの継承やメソッドのオーバーライドを禁止できる
- スーパークラスとサブクラスで同名のフィールドを定義すると「フィールドの隠蔽」が起こる。予期せぬエラーの原因となるため、避けるのが無難

---------------

記事は以上です。
最後までお読みいただき、ありがとうございました。

##  参考情報一覧

この記事は以下の情報を参考にして執筆しました。

- [オラクル認定資格教科書 JavaプログラマSilverSE11]
- [スッキリわかるJava入門 第４版]
- [パーフェクトJava 改訂3版]
- [プロになるJava]
- [11.1 継承と多態性（オーバーライド、抽象クラス、キャスト、ポリモーフィズムなど）～Java Basic編](最終更新 2023-11-05)(https://qiita.com/KenyaSaitoh/items/8fe2ebb65df4753bd907) (参照 2025-05-22)