第7回 継承とインターフェース

第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文をポリモーフィズムに直したらクラ
スが多くなってよけい理解しにくくなる
使うべきところに使う-バランスが大切