第7回 継承とインターフェイス ~オブジェクト指向の真髄に挑戦!~ 学習目標 継承とインターフェイスを利用したプログラ ムが書ける 継承を使う利点を説明できる Javaで継承を使ったプログラムが書ける ポリモーフィズムを使ったプログラムが書ける 7.1 継承 7.1.1 配列リストと連結リストの性能比較 7.1.2 継承 7.1.3 Javaで継承を使ったプログラムを書 く 7.1.1 配列リストと 連結リストの性能比較 public static void main(String[] args) { long startTime;//開始時間を保存する long endTime; //終了時間を保存する //-------配列リストの性能を測定する------//商品種類配列リストを生成する ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); startTime = System.currentTimeMillis();//開始時間を測定する //商品種類を1万件登録する for(int i=0;i<10000;i++){ itemTypeArrayList.add(new ItemType(1001,"コーラ",120)); } endTime = System.currentTimeMillis();//終了時間を測定する System.out.println("かかった時間は"+ (endTime - startTime) + "ミリ秒です"); //-------連結リストの性能を測定する------//商品種類連結リストを生成する ItemTypeLinkedList itemTypeLinkedList = new ItemTypeLinkedList(); startTime = System.currentTimeMillis();//開始時間を測定する //商品種類を1万件登録する for(int i=0;i<10000;i++){ itemTypeLinkedList.add(new ItemType(1001,"コーラ",120)); } endTime = System.currentTimeMillis();//終了時間を測定する System.out.println("かかった時間は"+ (endTime - startTime) + "ミリ秒です"); } 例題7-1 同じ意味の数字は定数に 前回のバグの原因 オブジェクト指向以前の問題です //商品種類を保存する配列を生成 itemTypeArray = new ItemType[10]; など、 //商品種類を保存するための変数を初期化する(実は不要) 全部で4ヶ所 //何も入っていないことをnullとして扱う あります。 for(int i=0;i<10;i++){ itemTypeArray[i] = null; } 同じ意味の数字は定数に 「同じ意味を何度も書かない」がプログラム 例題7-1 の基本です (ItemTypeArrayList.java) private int ARRAY_SIZE = 10000;//配列の大きさ 配列の大きさを //商品種類を保存する配列を生成 itemTypeArray = new ItemType[ARRAY_SIZE]; 保存しておく変数 を作ります //商品種類を保存するための変数を初期化する(実は不要) //何も入っていないことをnullとして扱う 変更する必要が for(int i=0;i<ARRAY_SIZE;i++){ なくなりますね。 itemTypeArray[i] = null; } ①ほとんど同じコードが2度 public static void main(String[] args) { long startTime;//開始時間を保存する long endTime; //終了時間を保存する 例題7-1 (Example7_1.java) //-------配列リストの性能を測定する------//商品種類配列リストを生成する ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); startTime = System.currentTimeMillis();//開始時間を測定する //商品種類を1万件登録する for(int i=0;i<10000;i++){ itemTypeArrayList.add(new ItemType(1001,"コーラ",120)); } endTime = System.currentTimeMillis();//終了時間を測定する System.out.println("かかった時間は"+ (endTime - startTime) + "ミリ秒です"); //-------連結リストの性能を測定する------//商品種類連結リストを生成する ItemTypeLinkedList itemTypeLinkedList = new ItemTypeLinkedList(); startTime = System.currentTimeMillis();//開始時間を測定する //商品種類を1万件登録する for(int i=0;i<10000;i++){ itemTypeLinkedList.add(new ItemType(1001,"コーラ",120)); } endTime = System.currentTimeMillis();//終了時間を測定する System.out.println("かかった時間は"+ (endTime - startTime) + "ミリ秒です"); } 同じ仕事 ②どうしたらメソッド化できる か? 今回は、この2つの仕事のメソッド化を考え ます 考えてみましょう 仕事が同じ意味をもつかどうか <A> •配列リストの性能を測る •配列リストを生成する •開始時間を測定する •配列リストに1万件追加する •終了時間を測定する •かかった時間を表示する <B> •連結リストの性能を測る •連結リストを生成する •開始時間を測定する •連結リストに1万件追加する •終了時間を測定する •かかった時間を表示する 仕事は同じ意味を持つ ただし、「リスト」という意味のカタマリがで きた場合 <仕事を> •「リスト」の性能を測る •「リスト」を生成する •開始時間を測定する •「リスト」 1万件追加する •終了時間を測定する •かかった時間を表示する 型が違う 型が違うので、今までの方法ではまとめら れない //---配列リスト版--ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); ・・・ itemTypeArrayList.add(new ItemType(1001,"コーラ")); まとめられない //---連結リスト版--ItemTypeLinkedList itemTypeLinkedList = new ItemTypeLinkedList(); ・・・ itemTypeLinkedList.add(new ItemType(1001,"コーラ")); 型が違う 問題の原因は配列リストと連結リストを違 う型として捉えていること 「リスト」という意味は同じだが、違うクラス ItemTypeArrayList + + + + Main add() delete() search() display() 1 意味は同じなのに 1 + main() ItemTypeLinkedList 1 1 + + + + add() delete() search() display() 7.1.2 継承 ①継承 ②継承をクラス図で表現する ①継承 「リスト」という意味のカタマリができれば、そのカ タマリに対して性能を測るプログラムができる <仕事を> •「リスト」の性能を測る •「リスト」を生成する •開始時間を測定する •「リスト」 1万件追加する •終了時間を測定する •かかった時間を表示する 継承を使って型の違う クラスの意味のカタマリをつくる 継承 「リスト」という抽象クラスを定義する ItemTypeList + + + + 共通の意味をクラスに add() delete() search() display() 抽象化 ItemTypeArrayList 共通の意味を持つクラス (商品種類のリスト) + + + + add() delete() search() display() ItemTypeLinkedList + + + + add() delete() search() display() ②クラス図で表現する ItemTypeListは ItemTypeArrayListの スーパークラス ItemTypeList ItemTypeLinkedListは ItemTypeListの サブクラス + + + + add() delete() search() display() ItemTypeLinkedList + + + + add() delete() search() display() ItemTypeArrayList + + + + add() delete() search() display() 同じ意味に見える Exampleは、新しい語彙である「リスト」クラ スに対してプログラムすることができます 配列リストか連結リストか意識しなくて済む より意味が明確なプログラムに ItemTypeList Main + main() 1 + + 2 + + add() delete() search() display() ItemTypeLinkedList + + + + add() delete() search() display() ItemTypeArrayList + + + + add() delete() search() display() 7.1.3 Javaで継承を 使ったプログラムを書く ①スーパークラスの変数への代入 ②Javaの記法 ③抽象クラスとインターフェイス ④継承vsインターフェイス ①スーパークラス の変数への代入 //---配列リスト版--ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); //---連結リスト版--ItemTypeLinkedList itemTypeLinkedList = new ItemTypeLinkedList(); ItemTypeArrayList インスタンス itemTypeArrayList ItemTypeLinkedList インスタンス itemTypeLinkedList 基本的に 型が違う変数には代入できない ①スーパークラス の変数への代入 //---配列版--ItemTypeList itemTypeList = new ItemTypeArrayList(); //---連結リスト版--ItemTypeList itemTypeList = new ItemTypeLinkedList(); ItemTypeArrayList インスタンス ItemTypeLinkedList インスタンス itemTypeList スーパークラスの変数には サブクラスのインスタンスも 代入することができる 入っている インスタンスによって切り替わる ItemTypeList itemTypeList;//スーパークラスの変数を用意する //---配列版--itemTypeList = new ItemTypeArrayList();//配列版インスタンスを代入 itemTypeList.add(new ItemType(1001,"コーラ")); //配列版インスタンスに追加されます //---連結リスト版--itemTypeList = new ItemTypeLinkedList();//連結リスト版インスタンスを代入 itemTypeList.add(new ItemType(1001,“コーラ”)); //連結リスト版インスタンスに追加されま す 変数に入っているインスタンスによって、自動的に切り替えられます Exampleは対象を考える必要はなくなり、コードが共通化できます 入っている インスタンスによって切り替わる ItemTypeArrayList インスタンス [0] [1] [2] [3] [4] [5] add() itemTypeList ItemTypeLinkedList インスタンス add() itemTypeList 22(id) 23(id) コーラ ソーダ 102番地 null ②Javaの文法(スーパークラス) //商品種類リストスーパークラス public class ItemTypeList { 例題7-2 (ItemTypeList.java) //商品種類を追加するメソッド public void add(ItemType addItemType){ //何も書かないくてよい } //商品種類を表示するメソッド public void display(){ スーパークラスには特別なことを //何も書かないくてよい } 記述する必要がありませんが、 … メソッドは空でも定義しておく } 必要があります ②Javaの文法(サブクラス) 例題7-2 (ItemTypeArrayList.java) //商品種類リスト配列実装クラス public class ItemTypeArrayList extends ItemTypeList{ ItemTypeListを継承 することを宣言します private int ARRAY_SIZE = 10;//配列の大きさ private ItemType[] itemTypeArray;//商品種類を保存する配列 //商品種類を追加するメソッド public void add(ItemType addItemType){ … } … } メソッドを実装します (スーパークラスと同じ メソッド名にする必要があります) これでメソッド化できる 引数にスーパークラスを定義することで、 どちらのサブクラスがきても同じ意味として 扱えるので、メソッド化することができる 例題7-2 (ItemTypeList.java) /** * 商品種類リストの性能を測る */ private static void performanceTest(ItemTypeList itemTypeList){ ・・・・・・ //商品種類を1万件登録する for(int i=0;i<10000;i++){ itemTypeList.add(new ItemType(1001,"コーラ",120)); } ・・・・・・ } ③抽象クラスとインターフェイス メソッド名を間違えるとどうなるか スーパークラス public class ItemTypeList { … //商品種類を表示するメソッド public void display(){ } … } サブクラス public class ItemTypeArrayList extends ItemTypeList{ //商品種類を表示するメソッド public void dispray(){ for(int i=0;i<ARRAY_SIZE;i++){ if(itemTypeArray[i] != null){ System.out.println(itemTypeArr… } } } … } 違うメソッドと解釈される サブクラスで実装されていない サブクラスで実装されていないと、自動的 にスーパークラスのメソッドが実行されてし まいます ItemTypeList itemTypeList = new ItemTypeArrayList(); //商品種類を追加する itemTypeList.add(new ItemType(1001,"コーラ")); //商品種類リストを表示する itemTypeList.display(); スーパークラス public class ItemTypeList { … //商品種類を表示するメソッド ?? サブクラス public void display(){ } public class ItemTypeArrayList extends ItemTypeList{… 何もおこらない … } } 実行! メソッド名の間違いを防ぐ コンパイルは通り、プログラムは動いてし まうので、バグの発見が大変です display()メソッドはスーパークラスでは空 にしてあるので、必ずサブクラスで実装し て欲しいです サブクラスで実装することを保証する方法があります 抽象クラス 例題7-2 (ItemTypeList.java) //商品種類リストスーパークラス public class ItemTypeList { … //商品種類を表示するメソッド public void display(){ } 例題7-3 (ItemTypeList.java) //商品種類リストスーパークラス public abstract class ItemTypeList { … //商品種類を表示するメソッドの定義 public abstract void display(); } } 空実装 定義だけする 「定義」と「実装」 「定義」ーメソッドを定義する 「実装」ーメソッドを実現する 例題7-3 (ItemTypeList.java) スーパークラス サブクラス public abstract class ItemTypeList { … //商品種類を表示するメソッドの定義 public abstract void display(); public class ItemTypeArrayList extends ItemTypeList{ } //商品種類を表示するメソッド public void display(){ for(int i=0;i<ARRAY_SIZE;i++){ if(itemTypeArray[i] != null){ System.out.println(itemTypeArr… } } } … } サブクラスで実装しないとコンパイルエラー 定義と実装を分けることの利点 定義と実装を分けることの利点を考えてみ ましょう 目的と手段を分離する 「定義」ーメソッドを定義する(目的) 「実装」ーメソッドを実現する(手段) 利用するクラスは「定義」に注目したプログラムが書ける 意味が明確なプログラムが書ける 抽象クラス 定義ができるのは「抽象クラス」 インスタンス化できないクラス 普通のクラスでは、実装しかできない public abstract class ItemTypeList { … //商品種類を表示するメソッドの定義 public abstract void display(); } インターフェイス Javaではメソッドの定義だけする方法とし て、インターフェイスが使える 例題7-4 抽象クラスとほとんど変わりません 抽象クラスとの違いは 変数がもてない 定義しかできない(実装ができない) インターフェイスを使う 例題7-4 (ItemTypeList.java) //商品種類リスト インターフェイス版 public interface ItemTypeList { //商品種類を追加するメソッドの定義 public void add(ItemType addItemType); スーパークラス … } インターフェイスは //商品種類リスト配列実装クラス メソッドが定義と決まっているので public class ItemTypeArrayList abstractは書きません ItemTypeList{ サブクラス implements //商品種類を追加するメソッド public void add(ItemType addItemType){ //実装します } } ④抽象クラス VS インターフェイス 機能で考えた場合どちらでも良い場合が 多い 抽象クラスでは変数や実装ができる インターフェイスは多重継承ができる 余裕のある人は、意味的な違いを考えてみましょう 7.2 if文vsポリモーフィズム 7.2.1 実装クラスの名前を表示する 7.2.2 どのように実装するか ①if文で実装する ②インスタンスの違いで分岐する 7.2.3 if文vsポリモーフィズム 7.2.1 実装クラスの名前を表示したい performanceTestメソッドの始めで、どの実 装クラスなのか表示するようにします /** * 商品種類リストの性能を測る */ private static void performanceTest(ItemTypeList itemTypeList){ //itemTypeListがArrayListなのかLinkedListなのかを表示する long startTime;//開始時間を保存する long endTime; //終了時間を保存する startTime = System.currentTimeMillis();//開始時間を測定する //商品種類を1万件登録する ... ①if文で実装する ところが... ItemTypeArrayList インスタンス スーパークラスの変数には サブクラスを代入できます ItemTypeLinkedL インスタンス itemTypeList ?? 逆に、代入してしまうと どのサブクラスを使っているかは 分からなくなってしまいます itemTypeList どのインスタンスが 入っているのか調べる方法 「instanceof」演算子を使います 例題7-6 (Example7_6.java) /** * 商品種類リストの性能を測る */ private static void performanceTest(ItemTypeList itemTypeList){ //itemTypeListがArrayListなのかLinkedListなのかを表示する if(itemTypeList instanceof ItemTypeArrayList){ System.out.println("実装クラスの名前:ItemTypeArrayList"); }else if(itemTypeList instanceof ItemTypeLinkedList){ System.out.println("実装クラスの名前:ItemTypeLinkedList"); } } [インスタンス名] instanceof [クラス名] 成立したらtrue,不成立ならfalse ②インスタンスの 違いで分岐する if文がなくても「実装クラスの名前を表示」 はできます 考えてみましょう クラスにメソッドを加える 実装クラス名を得るメソッドを作ります ItemTypeList Main + main() 1 + + 2 + + + add() delete() search() display() getName() 商品種類リストクラスに メソッドを加えます ItemTypeLinkedList + + + + + add() delete() search() display() getName() ItemTypeArrayList + + + + + add() delete() search() display() getName() インスタンスに場合分けを書く スーパークラスで 定義します //商品種類リスト 抽象クラス public abstract class ItemTypeList { ... //実装クラスの名前を得るメソッド public abstract String getName(); サブクラスで実装します //商品種類リスト配列実装クラス public class ItemTypeArrayList extends ItemTypeList{ … //実装クラスの名前を得るメソッド public String getName(){ return "ItemTypeArrayList"; } } } 例題7-6 (ItemTypeList.java) (ItemTypeArrayList.java) (ItemTypeLinkedList.java) //商品種類リスト連結リスト実装クラス public class ItemTypeLinkedList extends ItemTypeList{ … //実装クラスの名前を得るメソッド public String getName(){ return "ItemTypeLinkedList"; } } インスタンスで場合分けされる 「ポリモーフィズム」という仕組み 例題7-6 (Example7_6.java) /** * 商品種類リストの性能を測る */ private static void performanceTest(ItemTypeList itemTypeList){ //itemTypeListがArrayListなのかLinkedListなのかを表示する System.out.println("実装クラスの名前:"+itemTypeList.getName()); … } itemTypeListに入っているインスタンスによって 自動的に場合分けされます 7.2.3 if文 VS ポリモーフィズム どちらがよいのか議論してみましょう if文 VS ポリモーフィズム if文は拡張性が低い 要素が増えるたびにelse if文を追加しなけれ ばならない ポリモーフィズムならExampleを変えないで実 装クラスを追加できる ポリモーフィズムはクラスが増える 全部のif文をポリモーフィズムに直したらクラ スが多くなってよけい理解しにくくなる 使うべきところに使う-バランスが大切
© Copyright 2024 ExpyDoc