オブジェクト指向において、オブジェクト(クラス)の重要な概念として、継承(インヘリタンス)と多様化(ポリモーフィズム)があります。
自動車には乗用車、トラックなどの種類がありますが、どの種類でも、名称、メーカー、排気量など共通する属性があります。また、乗用車には、乗車定員やチャイルドシート数、トラックでは荷台面積や最大積載容量など独自の属性があります。
このような全体-部分の関係があるとき、自動車全般の共通する事項を定義するクラスをスーパークラスといい、乗用車やトラックなど、部分を対象とし、独自の事項を定義するクラスをサブクラスといいます。
そして、サブクラスがスーパークラスの定義を引き継ぐことを継承(インヘリタンス)といいます。
例えば、整数配列の合計を求めるメソッドを sumInt(整数配列)、実数配列の合計を求めるメソッドを sumFloat(実数配列) などと個別のメソッドにするのではなく、どのような型であっても一つのメソッド sum(配列) とできれば便利です。
また、ファールとは、野球であればストライクが1つ増えることであり、サッカーであれば相手側のボールになる(厳密ではない)ことだとすれば、同じメソッドでも機能が異なります。
このように同じメソッド名でも、(呼び出し側での)状況により、異なる処理をする仕組みを多様化(ポリモーフィズム)といいます。
継承・多様化の機能を Java で記述するには、オーバーライド、オーバーロードがあります。
大雑把には、オーバーライド = 継承、オーバーロード = 多様化 ですが厳密ではありません。異なる機能に使われることもあります。
オーバーライド:親クラスにあるメソッドを子クラスで再定義することによって、子クラス上で親クラスのメソッドを上書きすること
一般に、スーパーメソッドは引数なし、サブメソッドは引数ありで、引数の個数と型指定によりブクラスが一意に定まります。
(同じ引数個数、型指定で異なるサブクラスを設定することはできません。)
オーバーロード:引数の数とか型とかが違う、同じ名前の関数を複数個、用意すること
「学生」をスーパークラス、「学生」のうちの「留学生」をサブクラスとします。
Javaでは、スーパークラスで定義されたことをサブクラスで再度定義することをオーバーライドといいます。留学生クラスは学生クラスをオーバーライドです。
サブクラス次のように記述します。
class サブクラス名 extends スーパークラス名
class 学生クラス { // スーパークラス
String 氏名 = "阿部"; // スーパークラスの構造体とその値
String 学部 = "文学部";
int 年齢 = 20;
};
// これがオーバーライド
class 留学生クラス extends 学生クラス { // サブクラス (extends でスーパークラスを指定)
String 国籍 = "米国"; // 新規の要素
String 氏名 = "Smith"; // スーパークラスの変更
}; // ここで指定しないものはスーパークラスで指定した値を継承
public class Main {
public static void main(String[] args) {
学生クラス 学生 = new 学生クラス(); // スーパークラスの構造体
System.out.println("学生:" // 学生:阿部, 文学部. 20
+ 学生.氏名 + ", " + 学生.学部 + ". " + 学生.年齢);
留学生クラス 留学生 = new 留学生クラス(); // サブクラスの構造体
System.out.println("留学生:" // 留学生:Smith, 文学部. 20, 米国
+ 留学生.氏名 + ", " + 留学生.学部 + ". " + 留学生.年齢 + ", " + 留学生.国籍);
}
}
ここでは、ある倉庫の在庫管理のうち、在庫減に関する部分を例にします。
倉庫から商品が出ることを「出荷」、そのうち、客先に出荷することを「売上」、社内の別倉庫に転送することを「転送」ということにします。
出荷はスーパークラス、売上と転送はサブクラスの関係があります。
mainメソッドから出荷量が受け渡されたとき、
出荷で共通メソッドである「在庫量 = 在庫量 - 出荷量」と「出荷量 > 在庫量 でのエラー処理」をします。
売上では「売上量 = 出荷量」の処理をするだけでよいし、転送では「何もしない」でよいことになります。
の処理になります。
class 出荷 {
int 在庫量 = 10;
int 売上量 = 0;
int 出荷量 = 0;
public 出荷(int 出荷量) {
this.出荷量 = 出荷量;
在庫量 = 在庫量 - 出荷量;
if (在庫量 < 0) {
System.out.println("エラー! 出荷量 > 在庫量");
System.exit(0);
}
}
}
class 売上 extends 出荷 {
public 売上(int 出荷量) {
super(出荷量); // 出荷量に関するスーパークラスの処理を継承
this.出荷量 = 出荷量;
売上量 = 出荷量;
}
}
class 転送 extends 出荷 {
public 転送(int 出荷量) {
super(出荷量);
this.出荷量 = 出荷量;
}
}
public class Main {
public static void main(String[] args) {
売上 売上結果 = new 売上(3);
System.out.println("売上:出荷量=" + 売上結果.出荷量 // 売上:出荷量=3, 在庫量=7, 売上量=3
+ ", 在庫量=" + 売上結果.在庫量 + ", 売上量=" + 売上結果.売上量);
転送 転送結果 = new 転送(4);
System.out.println("転送:出荷量=" + 転送結果.出荷量 // 転送:出荷量=4, 在庫量=6, 売上量=0
+ ", 在庫量=" + 転送結果.在庫量 + ", 売上量=" + 転送結果.売上量);
}
}
整数での合計、実数での合計を、引数の型指定により同じメソッド名で使えるようにします。
class 合計クラス {
public int 合計(int a, int b) {
return a + b;
}
public double 合計(double a, double b) { // 上と同じメソッド名 オーバーロード
return a + b;
}
}
public class Main {
public static void main(String[] args) {
合計クラス 結果 = new 合計クラス();
System.out.println("整数:" + 結果.合計( 1, 2)); // 3
System.out.println("実数:" + 結果.合計( 1.3, 2.5)); // 3.8
}
}
class 学生クラス {
String 氏名;
int 年齢;
public 学生クラス() { // パラメタなし。氏名・年齢は省略値
this.氏名 = "阿部"; // this とは「ここでの」のような意味。阿部に固定
this.年齢 = 20;
};
public 学生クラス(String 氏名, int 年齢) { // 同じコンストラクタ名。パラメタあり。氏名・年齢は Mainで与える
this.氏名 = 氏名; // this は引数、すなわち Main で与えた値にすることを示す。
this.年齢 = 年齢;
};
}
public class Main {
public static void main(String[] args) {
学生クラス 学生1 = new 学生クラス();
System.out.println(学生1.氏名 + ", " + 学生1.年齢); // 阿部, 20
学生クラス 学生2 = new 学生クラス("井上", 21);
System.out.println(学生2.氏名 + ", " + 学生2.年齢); // 井上, 21
}
}
整数での四則演算(ここでは加算と乗算だけ)を例にします。
加算.計算(4, 2);
乗算.計算(4, 2));
のように「計算」メソッドを多様化しています。
// 抽象クラス
abstract class 四則演算 {
public int 計算(int x, int y) {
return 演算(x, y);
}
abstract protected int 演算(int x, int y);
}
// 加算クラス(サブクラス)
class 加算 extends 四則演算 {
@Override
protected int 演算(int x, int y) { return (x+y); }
}
// 乗算クラス(サブクラス)」
class 乗算 extends 四則演算 {
@Override
protected int 演算(int x, int y) { return (x*y); }
}
// プログラムの実行]
public class Main {
public static void main(String[] args) {
// 加算クラスの実行
四則演算 加算 = new 加算();
int 結果 = 加算.計算(4, 2);
System.out.println(結果);
// 乗算クラスの実行
四則演算 乗算 = new 乗算();
System.out.println(乗算.計算(4, 2));
}
}