Javaのオーバーライドとは
Javaで使われるオーバーライドは、クラスを継承する際に、スーパークラス(親クラス)のメソッドをサブクラス(子クラス)で定義し直すことです。すでに存在するクラスで、あるメソッドだけを変更して使用したいという場合に使えます。
この記事では、Javaのオーバーライドを使うメリットや、似ている用語であるオーバーロードとの違い、使う際の注意などを、実際のオーバーライドのサンプルコードも紹介しながらわかりやすく解説していきます。
オーバーライドを使うメリット
ここでは、オーバーライドを使うメリットについて見ていきます。少ない変更点でメソッドを上書きできるという再利用のしやすさと、保守のしやすさが挙げられます。
再利用が簡単にできる
オーバーライドを使うことで、既存のクラスのメンバ変数や他のメソッドは変更せずに、上書きしたいメソッドだけ変更して使うことができます。これにより、既存クラスの再利用が簡単にできるのがメリットの1つです。
オーバーライドによる再利用で、主にオブジェクト指向プログラミングの「ポリモーフィズム(多様性)」の考え方を実現することができます。ポリモーフィズムは、ある1つのメソッドの呼び出しで、オブジェクトごとに異なる動きをするという概念です。
保守がしやすい
オーバーライドを使ったメソッドは、サブクラスで新たに定義した処理内容を変更したい際に、スーパークラスや他のサブクラスに影響を与えずに変更を加えることができます。また、スーパークラスの処理内容を変更したい時にも、サブクラスまで変更を行う必要がありません。
これにより、クラスの処理内容を変更するなどの保守が行いやすくなるというメリットがあります。
オーバーライドの使い方
ここでは、オーバーライドのルールなどを具体的に解説し、似た用語であるオーバーロードの違いについても説明します。
オーバーライドのルール
スーパークラスを継承したサブクラスでメソッドをオーバーライドする際に、オーバーライドであるとみなされるためのルールがあります。
■オーバーライドになる条件 ・スーパークラスのメソッドと、戻り値の型・メソッド名・引数の型・引数の数・引数の順番が同じである
例えば、スーパークラスAで「move」というメソッドを定義し、クラスAを継承するサブクラスB、サブクラスCがあるとします。サブクラスB、サブクラスCでは、スーパークラスAが持つ「move」メソッドをそのまま使用できます。
しかし、サブクラスBの「move」メソッドは独自に処理を変えたいという場合があります。その時にオーバーライドすることで、メソッド名と引数をそのまま使い、処理内容はクラスBに適した内容に変更することが可能です。
つまり、スーパークラスAを継承したサブクラス全てで同一のメソッドやメンバ変数を使用しながら、必要に応じて任意のサブクラスのメソッドの処理内容だけを再定義することができるのです。
オーバーロードとの違いとは
Javaで同じような用語に、オーバーロードがあります。言葉も似ており、どちらもメソッドの扱いに関するもので間違われやすいため、よく理解しておきましょう。
Javaでは、引数の型、引数の数が完全に一致しない場合、同名の異なるメソッドを定義することができます。この特徴を使って、同一クラスに、同名の異なるメソッドを定義することをオーバーロードと呼びます。
■オーバーロードになる条件 以下のいずれかの条件を満たすことで、同名のメソッドを同じクラス内に定義できます。
・引数の数が異なっている ・引数の種類が異なっている ・引数のデータ型と数が同じでも、順番が異なっている
このように、オーバーロードはオーバーライドとは異なる概念です。言葉は似ていますが、違いがわかってくれば使い分けできるようになります。慣れないうちは混同しないように注意しましょう。
実際のコードで確認しよう
ここでは、具体的にオーバーライドのサンプルコードを見ながら解説していきます。併せて、スーパークラスのメソッドをそのまま使うsuper句についても説明します。
オーバーライドのサンプルコード
以下のサンプルコードをご覧ください。
スーパークラスAnimalには、「体当たり」を出力するattack()メソッドがあります。それをサブクラスCatとサブクラスDogがオーバーライドしています。Catクラスは「引っ掻く」でattack()メソッドをオーバーライドし、Dogクラスは「噛み付く」でオーバーライドしています。
次に、これらのクラスのインスタンスを作成し、それぞれのattack()メソッドを呼び出します。
サンプルコード:
class Animal {
public void attack() {
System.out.println("体当たり");
}
}
class Cat extends Animal {
@Override
public void attack() {
System.out.println("引っかく");
}
}
class Dog extends Animal {
@Override
public void attack() {
System.out.println("噛み付く");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Animal();
Animal animal2 = new Cat();
Animal animal3 = new Dog();
animal1.attack();
animal2.attack();
animal3.attack();
}
}
実行結果:
体当たり
引っかく
噛み付く
実行結果を見るとわかる通り、Animalクラスのattack()メソッドでは「体当たり」が出力されますが、Catクラスでは「引っかく」、Dogクラスでは「噛み付く」が出力されます。このように、サブクラスにそれぞれ独自のattack()メソッドを定義できました。
superを使ったサンプルコード
オーバーライドする際でも、スーパークラスが持つメソッドをそのまま使うことが可能です。
以下のサンプルコードをご覧ください。 この例では、Animalクラスに、「体当たり」を出力するattack()メソッドを用意し、それをサブクラスCatがオーバーライドしています。
しかし、Catのattack()メソッドはsuper句を使うことで、親クラスであるAnimalクラスのものをそのまま使います。 次に、これらのクラスのインスタンスを作成し、attack()メソッドを呼び出します。
サンプルコード:
class Animal {
public void attack() {
System.out.println("体当たり");
}
}
class Cat extends Animal {
@Override
public void attack() {
super.attack(); // superで親クラスのメソッドをそのまま使う
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Animal();
Animal animal2 = new Cat();
animal1.attack();
animal2.attack();
}
}
実行結果:
体当たり
体当たり
実行結果から分かる通り、サブクラスCatではattack() メソッドをオーバーライドしましたが、super句を付与することにより、スーパークラスAnimalのattack() メソッドをそのまま使い、「体当たり」を出力しました。
オーバーライトを使う際の注意
ここでは、オーバーライドを使う際に注意すべき、修飾子の指定・抽象クラスの継承・アノテーションの使用について解説します。
修飾子の指定
サブクラスでオーバーライドしようとするスーパークラスの元メソッドに、final修飾子が指定されている場合、オーバーライドができません。サブクラスに処理を変更されたくないメソッドには、オーバーライドできないようにfinal修飾子を指定しましょう。
また、サブクラスでオーバーライドするメソッドのアクセス修飾子は、スーパークラスの元メソッドと同じか緩い制限にする必要があります。例えば、スーパークラスがprotectedであれば、サブクラスはprotectedかpublicを指定可能です。
抽象クラスを継承して具象クラスを定義するとき
継承して利用されることを前提としたクラスである抽象クラス(abstractクラス)を継承した具象クラスを定義する際には、抽象メソッド(abstractメソッド)を必ずオーバーライドしなければなりません。
抽象メソッドは中身が定義されていないメソッドであるため、継承したクラスに抽象メソッドが残ったままでは具象クラスにならず、オブジェクトを作成することができません。
アノテーションを付ける
オーバーライドするメソッドに付ける「@Override」の記述は「アノテーション」と呼ばれます。アノテーション「@Override」は、サブクラスに「オーバーライドされたメソッドが定義されている」という注釈を付けるものです。
アノテーションを記述することにより、メソッド名の記述誤りなどのミスを防ぐことができます。 次のサンプルコードをご覧ください。
サンプルコード:
class Cat extends Animal {
@Override
public void attack() {
System.out.println("引っかく");
}
}
ここでは、「attack()」メソッドをオーバーライドしていますが、もしもメソッド名を「atack()」と書き間違えてしまうと、コンパイル時に「atack()」という新しいメソッドを定義したと解釈します。オーバーライドができず、エラーも出ないので誤りに気付きません。
しかし、アノテーションがあれば、オーバーライドしたはずの「attack()」メソッドがないことでコンパイルエラーとなり、誤りに気付くことができます。また、多くの開発環境ではコンパイルより前に開発ツール側でエラーが出て教えてくれます。
コードを読んだ時にも、単純にオーバーライドしていることが視認しやすいという効果もあります。オーバーライドする際にはもれなく「@Override」アノテーションを付けるようにすると良いでしょう。
Javaのオーバーライドを活用してコーディングを効率化しよう
ここまでJavaのオーバーライドについて解説してきました。オーバーライドを活用することで、クラスの継承におけるコーディングの効率化や構造のシンプル化を図ることができます。
また、オーバーライドを使うことでポリモーフィズム(多様性)を実現することができ、オブジェクト指向プログラミングの理解を深めることにも役立ちます。ぜひ、オーバーライドを使ってコーディングしてみてください。
編集部オススメコンテンツ
アンドエンジニアへの取材依頼、情報提供などはこちらから