アルゴリズムとプログラミング (Algorithms and Programming) 第11回:継承の実装 継承の実装: extends キーワード コンストラクタ 継承とメソッドのオーバーライド,可視性 抽象クラス (abstractキーワード) 講義資料等: http://www.pe.titech.ac.jp/~watanabe/lecture/ap/index-j.html
継承の実装 Vehicle(乗り物) Plane サブクラスは、スーパークラスの全てのフィールドとメソッドを含んでいる。 - speed: double # changeSpeed(s:double) # getSpeed(): double サブクラスは、スーパークラスの全てのフィールドとメソッドを含んでいる。 (実際にアクセスできるかは、アクセス修飾子に従う) サブクラス Plane - altitude: double # takeOff () # land ()
継承の実装:extendsキーワード Vehicle(乗り物) Plane スーパークラス - speed: double # changeSpeed(s:double) # getSpeed(): double class Plane extends Vehicle { private double altitude; protected void takeOff() { 処理(スーパークラスのメソッドも呼べる); } protected void land() { 処理; サブクラス Plane - altitude: double # takeOff () # land ()
スーパークラスへのアクセス Vehicle(乗り物) Plane スーパークラス - speed: double # changeSpeed(s:double) # getSpeed(): double public static void main(略) { Plane o = new Plane(); o.changeSpeed(0.0); for(int i=0;i<=300;i+=50){ o.changeSpeed(i); o.takeOff(); } System.out.println("離陸!"); サブクラス Plane - altitude: double # takeOff () # land ()
全てのクラスのスーパークラス Objectクラス extendsキーワードが無い全てのクラスは、自動的にObjectクラスのサブクラスとなる。従って、全てのクラスはObjectクラスのフィールドとメソッドを暗黙的に継承している。 Vehicle(乗り物) - speed: double # changeSpeed(s:double) # getSpeed(): double Plane - altitude: double # takeOff () # land ()
継承とコンストラクタ サブクラスのコンストラクタを呼び出すと.. まずスーパクラスのコンストラクタが実行される (何も指定しなければデフォルトコンストラクタが呼ばれる.引数は無し) 次にサブクラスのコンストラクタが実行される
サブクラスのコンストラクタ(例) ここにsuper();が省略 されていると考える class X { int x; X() { SampleAP0901.java class X { int x; X() { x = 100; } class Y extends X { int y; Y() { y = 200; ここにsuper();が省略 されていると考える
サンプルプログラム(続き) x= 100 y= 200 class SampleAP0901 { SampleAP0901.java続き class SampleAP0901 { public static void main(String[] args) { Y o = new Y(); System.out.println("x= " + o.x ); System.out.println("y= " + o.y ); } 実行結果 x= 100 y= 200
親のコンストラクタを指定する場合 Superキーワード (thisキーワードとの併用は不可) SampleAP0902.java class X { int x; X(int a) { x = a; } class Y extends X { int y; Y(int a, int b) { super(a); y = b; コンストラクタでsuperを使うときは 最初の行でしか使えない!
SampleAP0902.java続き class SampleAP0902 { public static void main(String[] args) { Y o = new Y( 1, 2 ); System.out.println("x= " + o.x ); System.out.println("y= " + o.y ); } 実行結果 x= 1 y= 2
暗黙の親コンストラクタ呼び出し ここにsuper();が省略 されていると考える class X { int x; X() { SampleAP0901.java(再) class X { int x; X() { x = 100; } class Y extends X { int y; Y() { y = 200; ここにsuper();が省略 されていると考える
継承を禁止するfinalキーワード サブクラスの宣言を禁止! コンパイルエラー! final宣言したクラスは、extendsで final class X { int x; } class Y extends X { int y; サブクラスの宣言を禁止! コンパイルエラー! final宣言したクラスは、extendsで サブクラスを作ることが禁止される
メソッドのオーバーライド オーバーライド(override) オーバーロード (overload) 親クラスと子クラスのそれぞれに、 メソッド名、引数、戻り値が全て同一で内容が異なるメソッドを定義すること 既出 混同注意! オーバーロード (overload) ひとつのクラス内に、メソッド名が同じで、引数の型及び数が異なるメソッドを定義すること
オーバーライドの目的 オーバーライドの必要性 Vehicle(エンジン付き乗り物) もともと、サブクラスにはスーパークラスのフィールドとメソッドが全て自動的に引き継がれている。 - speed: double # getSpeed(): double # startEngine () しかし、引き継がれているメソッドの処理内容を、サブクラスで再定義(変更)したい場合がある 。 Plane - altitude: double # takeOff () # startEngine () オーバーライドの必要性
オーバーライドと可視性 子クラスでオーバーライドするメソッドの可視性は、親クラスのメソッドの可視性により制限される public オーバーライドされるスーパークラスのメソッドに指定されている修飾子 オーバーライドするサブクラスのメソッド public public指定が必要 protected protectedかpublicが必要 private アクセス不可につき、オーバーライド不可 無指定 private指定は不可 親クラスでprotectedが指定されているメソッドを子クラスでオーバーライドするにはprotectedかpublic指定が必要
親・子クラスにおける オブジェクト参照型変数の互換性 Javaのきまり: 型の違いに厳しいJavaですが.. 子クラスのインスタンスを指すオブジェクト参照型変数を、親クラスの参照変数に代入することができる。 代入後も、インスタンスの出身が子クラスであることを忘れずに記憶しておく仕組みになっている。 1つのオブジェクトが、あたかも複数の型を持つかのように見せることができる。
オブジェクト指向3原則:ポリモーフィズム (再) (メソッドのオーバーライドと参照型変数の 互換性によって実現される) 乗り物 ジェット機クラスのインスタンスを、乗り物オブジェクトとして扱うことができる。 飛行機 自動車 ジェット機クラスのインスタンスに対する操作を、乗り物に対する操作として記述できる ジェット機 プロペラ機 このような記述の仕方にどんなメリットがあるの? ジェット機は飛行機でもあり、乗り物でもある
操作名は同じだが、実際の操作内容はそれぞれ異なる ジェット機 プロペラ機 乗り物 #動力を始動する() 飛行機 自動車 #動力を始動する() #動力を始動する() 操作名は同じだが、実際の操作内容はそれぞれ異なる ジェット機 プロペラ機 #動力を始動する() #動力を始動する() メソッドのオーバーライド
ジェット機クラスのインスタンスを生成する :ジェット機 new ジェット機(); このインスタンスを乗り物オブジェクトと見なす (乗り物オブジェクト変数 v1 に格納する) 乗り物 v1 = new ジェット機(); 乗り物クラス ジェット機クラスのインスタンスへの参照 動力を始動する()メッセージを送る この記法は、ジェット機に限らず、プロペラ機にも自動車にも、将来的に追加される乗り物にもそのまま変更なく使える。 v1.動力を始動する() 乗り物オブジェクトの操作が呼び出されるように記述されているが、自動的にジェット機オブジェクトに対する操作が呼び出される 上位概念(スーパークラス)オブジェクトに対する操作として記述しておくと、サブクラスの細かい修正や追加などに対して強いプログラムになる(保守性が高い)
ジェット機クラスのインスタンスを生成する :ジェット機 new ジェット機(); このインスタンスをジェット機オブジェクト変数 j1 に格納する ジェット機 j1 = new ジェット機(); ジェット機クラス ジェット機クラスのインスタンスへの参照 動力を始動する()メッセージを送る j1.動力を始動する() この記法では、プロペラ機や自動車に変更したり、さらには将来的に追加される乗り物に対しては、そのままでは使えない。プログラムの記述を変更する必要がある!
オブジェクト指向プログラミングの有効性! ポリモーフィズムの意味と効果 ポリモーフィズム(polymorphism)の意味: 1つのオブジェクトが複数の型を持つ ← 継承により実現 ポリモーフィズムの活用の仕方: 上位概念(スーパークラス)への操作としてプログラムを記述する その効果: サブクラスの細かい修正や追加などに対して強い(保守性が高い)プログラムになる オブジェクト指向プログラミングの有効性!
参照型変数への代入 (例) class X { // スーパー(親)クラス int x; } SampleAP1001.java class X { // スーパー(親)クラス int x; } class Y extends X { // サブ(子)クラス int y; class SampleAP1001 { public static void main(String[] args){ X o = new Y();//サブクラスYのインスタンスを親クラスX } //の参照型変数へ代入できる!
メソッドのオーバーライド(例) class Shape { //2次元図形の面積を表現するクラス SampleAP1002.java class Shape { //2次元図形の面積を表現するクラス double a; //一般に、面積を計算するには、縦×横や底辺×高さ/2など、 double b; //2つの辺の長さを与える必要があると考え、変数を2つ用意する Shape(double x, double y) { a = x; b = y; } double getArea() { //ここでは具体的な定義を与えないことにする。 return 0.0; //具体的な定義はサブクラスで与える。
オーバーライド オーバーライド class Triangle extends Shape { //三角形 SampleAP1002.java続き class Triangle extends Shape { //三角形 Triangle(double x, double y); super(x,y); // x,yとしては底辺と高さを与える } double getArea() { return ( a * b / 2 ); class Rectangle extends Shape { //四角形 Rectangle (double x, double y){ super(x,y); //x,yとしては各辺の長さを与える return ( a * b ); オーバーライド オーバーライド
public static void main(String[] args) { SampleAP1002.java続き class SampleAP1002 { public static void main(String[] args) { Shape o1 = new Shape( 10.0, 10.0 ); Shape o2 = new Triangle( 10.0, 10.0 ); Shape o3 = new Rectangle( 10.0, 10.0 ); System.out.println("o1の面積は" + o1.getArea() ); System.out.println("o2の面積は" + o2.getArea() ); System.out.println("o3の面積は" + o3.getArea() ); }
引数に与えた数値はo1~o2まで全て同じだが、それぞれオーバーライドされたgetArea()メソッドが、異なる処理を実行している。 実行結果 o1の面積は0.0 o2の面積は50.0 o3の面積は100.0 引数に与えた数値はo1~o2まで全て同じだが、それぞれオーバーライドされたgetArea()メソッドが、異なる処理を実行している。
抽象クラス: 抽象メソッドを持つためオブジェクト生成できないクラス 抽象クラス: 抽象メソッドを持つためオブジェクト生成できないクラス 乗り物 クラス名をイタリックにすると 抽象クラス -スピード #スピードを変える() #スピードを表示する() 乗り物クラスのインスタンスは 存在しないことが保証される 飛行機 飛行機クラスでは、依然として スピードを表示する()操作は必須 であり、乗り物クラスは使われる。 -高度 #離陸する() #着陸する() #高度を表示する()
抽象メソッド: abstractキーワード 抽象クラスの実装 親クラスではメソッド名だけ定義して、子クラスにおいて、オーバーライドにより具体的な処理内容を記述する 抽象メソッド: abstractキーワード 具体的な処理内容が与えられていないため、インスタンスの作成は不可
抽象クラスの使用例 abstract class Shape { //抽象クラス double a; double b; SampleAP1003.java abstract class Shape { //抽象クラス double a; double b; Shape(double x, double y) { a = x; b = y; } //抽象メソッドとして定義(この方が自然) abstract double getArea(); 抽象メソッドが1つでもあるとそのクラスは抽象クラス →サブクラスで具体的な処理内容を与えて使用する 抽象クラスだからといって、全てが抽象クラスである必要は無い
class Triangle extends Shape { //三角形 Triangle(double x, double y); SampleAP1003.java続き この部分はSampleAP1002.javaと同じ class Triangle extends Shape { //三角形 Triangle(double x, double y); super(x,y); // x,yとしては底辺と高さを与える } double getArea() { return ( a * b / 2 ); class Rectangle extends Shape { //四角形 Rectangle (double x, double y){ super(x,y); //x,yとしては各辺の長さを与える return ( a * b );
抽象クラスのインスタンス化を削除 class SampleAP1003 { SampleAP1003.java続き class SampleAP1003 { public static void main(String[] args) { Shape o2 = new Triangle( 10.0, 10.0 ); Shape o3 = new Rectangle( 10.0, 10.0 ); System.out.println("o2の面積は" + o2.getArea() ); System.out.println("o3の面積は" + o3.getArea() ); } 抽象クラスのインスタンス化を削除
まとめ1 継承の実装: extends キーワード 継承とコンストラクタ: superキーワード 継承の禁止: finalキーワード
まとめ2 継承とメソッドのオーバーライド 抽象メソッド、抽象クラス より保守性の高いプログラムの実現 世の中で既に作成され公開されている各種のJavaクラスライブラリはこのような考え方で作成されている。適当なクラスを継承してカスタマイズすることにより、極めて簡単に自分独自の問題を解決することができる。