オブジェクト指向において、オブジェクト(クラス)の重要な概念として、継承(インヘリタンス)と多様化(ポリモーフィズム)があります。
自動車には乗用車、トラックなどの種類がありますが、どの種類でも、名称、メーカー、排気量など共通する属性があります。また、乗用車には、乗車定員やチャイルドシート数、トラックでは荷台面積や最大積載容量など独自の属性があります。
このような全体-部分の関係があるとき、自動車全般の共通する事項を定義するクラスをスーパークラスといい、乗用車やトラックなど、部分を対象とし、独自の事項を定義するクラスをサブクラスといいます。
そして、サブクラスがスーパークラスの定義を引き継ぐことを継承(インヘリタンス)といいます。
例えば、整数配列の合計を求めるメソッドを 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)); } }