はじめに
これから継承について説明しますが、まずはもっとも重要なことを覚えておいてください。継承とはただのコードの書き方の一つであり、継承をすればいいというわけではありません。継承を使うべきかどうかは、状況に応じて適切に判断することが重要です。
継承の主な使用目的
1. メソッドで処理を決定しない
継承を使う主な目的の1つは、処理の内容をメソッドが決定しないようにして、処理を柔軟に変更できるようにすることです。以下の例を見てください。
// 足し算を行うクラス
class AddCalculator {
public int calculate(int a, int b) {
return a + b;
}
}
// 引き算を行うクラス
class SubCalculator {
public int calculate(int a, int b) {
return a - b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int[] numbers = {3, 4, 1, 2, 5};
int result = execute(calculator, numbers, 1, 4);
System.out.println(result);
int result2 = execute(calculator, numbers, 0, 3);
System.out.println(result2);
}
// 足し算を実行するメソッド
public int executeAdd(AddCalculator addCalculator, int[] numbers, int i, int j) {
int result = calculator.calculate(numbers[i], numbers[j]);
return result;
}
// 引き算を実行するメソッド
public int executeSub(SubCalculator subCalculator, int[] numbers, int i, int j) {
int result = calculator.calculate(numbers[i], numbers[j]);
return result;
}
}
これは、足し算と引き算を行うクラスがあり、それぞれのクラスを使って計算を行っています。このとき、足し算と引き算以外に掛け算や割り算を行いたい場合、新たに掛け算や割り算を行うクラスを作成し、executeMul
やexecuteDiv
を作成する必要があります。計算を行うクラスはどちらにしても増やす必要がありますが、executeXX
メソッドは計算方法とは関係のない配列の要素を取得して計算機に渡す処理のため共通の処理であり増やす必要がないです。このような場合、継承を使うことで、executeXX
メソッドを共通化することができます。
// 計算を行うインターフェース
interface Calculator {
public int calculate(int a, int b);
}
// 足し算を行うクラス
class AddCalculator implements Calculator {
@override
public int calculate(int a, int b) {
return a + b;
}
}
// 引き算を行うクラス
class SubCalculator implements Calculator {
@override
public int calculate(int a, int b) {
return a - b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new AddCalculator();
int[] numbers = {3, 4, 1, 2, 5};
int result = execute(calculator, numbers, 1, 4);
System.out.println(result);
int result2 = execute(calculator, numbers, 0, 3);
System.out.println(result2);
}
// 計算を実行するメソッド
public int execute(Calculator calculator, int[] numbers, int i, int j) {
int result = calculator.calculate(numbers[i], numbers[j]);
return result;
}
}
また、これを利用することでメソッドの処理の一部を引数の値によって、メソッド内のコードを変更せずに変更することができます。Javaの標準ライブラリにもこのような例があります。例えば、java.util.Comparator
インターフェースは、compare
メソッドを実装することで、Collections.sort
メソッドでソートの方法を変更することができます。
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("banana", "apple", "orange");
// Comparatorを使ってリストをソート
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
System.out.println(list); // Output: [apple, banana, orange]
// 逆順にソート
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}
});
System.out.println(list); // Output: [orange, banana, apple]
}
}
この例では、Comparator
インターフェースを使ってリストをソートしています。最初は通常の昇順でソートし、次に逆順でソートしています。
2. コードのフォーマット
継承を使う目的の1つは、コードのフォーマットを統一することです。多人数での開発では、コードのフォーマットを厳密に統一することが難しい場合があります。また、フレームワークなどで特定のクラスを作成してもらう必要がある場合にも、継承を使うことでコードのフォーマットを統一しやすくなります。
先ほどの例では、calculate
メソッドは必ず実装しなければならないため、calculate
メソッドを実装しないクラスを作成することができません。これにより、calculate
メソッドを実装しないクラスを作成することを防ぐことができます。
多人数開発では、他の人が作成したクラスを使うときに、calculate
メソッドが実装されていることが保証されるため、安心して使うことができます。また、フレームワークなどでは、そもそも実装されている前提で作成することになるので、継承を使う必要があります。
3. 差分プログラミング
継承を使う目的の1つは、差分プログラミングを行うことです。これは、コードの一部をoverrideすることで、新たな機能を追加することです。以下の例を見てください。
// 基本的なプリント機能を持つクラス
class BasePrinter {
public void print() {
System.out.println("Base print");
}
}
// カスタムプリント機能を持つクラス
class CustomPrinter extends BasePrinter {
@Override
public void print() {
System.out.println("Custom print");
}
}
public class Main {
public static void main(String[] args) {
BasePrinter printer = new CustomPrinter();
printer.print(); // Output: Custom print
}
}
この例では、BasePrinter
クラスのprint
メソッドをCustomPrinter
クラスでオーバーライドしています。これにより、CustomPrinter
クラスのインスタンスを使ってprint
メソッドを呼び出すと、BasePrinter
クラスのprint
メソッドではなく、CustomPrinter
クラスのprint
メソッドが実行されます。このように、継承を使うことで、既存のコードを変更せずに新たな機能を追加することができます。
ただし、同じだと思われる処理をoverrideすることで、予期せぬバグを発生させる可能性があるため、overrideする際は注意が必要です。
基本的には、他のクラスから参照されないメソッドやフィールドをoverrideして限定的に使用するようにしてください。
これは、一時的に処理を変更するための手段であり通常の開発では使わないようにしましょう。
さいごに
最初に書きましたが、継承はただのコードの書き方の一つです。継承を使うべきかどうかは、状況に応じて必要であれば使えばいいということです。継承を使うことが出来るから使うのではなく、継承を使うことで得られるメリットがあるから使うという考え方が重要です。