• 検索結果がありません。

extends (*) (*) extend extends 2

N/A
N/A
Protected

Academic year: 2021

シェア "extends (*) (*) extend extends 2"

Copied!
31
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラム理論と言語 2007-2-1-4

1

クラス階層

1.1

インタフェースとの対比

(2)

クラス階層

「A

extends

B」

: 『AはBを継承し拡大』

(*)

メソッドのみならずフィールドも追加可

(*)「AはBを extendする」は、通常、「AはBを継承する」と言う 本講義では気にせずに、「拡大」という言葉も使う。既存クラスにメソッドや属性を追加する気分 は「拡大」の方が馴染むとの単純な理由。実際、オブジェクトが占有するデータ領域はextends される。 本講義では、抽象メソッドがない場合でも、インスタンス化しない場合は抽象クラスとして明示的に扱 う。オブジェクトの外延を考えるときは、むしろこの方がすっきりする。

(3)

クラス階層の例

class

Person {

private String name;

String name() {return name;}

Person(String name) {

this.name=name;

}

}

class Worker extends Person {

private int salary;

Worker(String name, int salary) {

super(name); //

親のコンストラクタ

this.salary = salary;

}

int salary() {return salary;} //

固有なメソッド

String who() {

return "I am a worker, "+name();

//

親のメソッドを継承

}

}

(4)

クラス階層の例1

(RDB) entitiy-relationship diagram with ISA

楕円は属性、 四角はクラス ISA =”extends”

(5)

クラス階層の例2:ユーティリティは階層化された

クラスの集まり

クラス Integer java.lang java.lang.Object | +--java.lang.Number | +--java.lang.Integer すべての実装インタフェース: Comparable, Serializable

クラス

ArrayList

java.util

java.lang.Object

|

+--java.util.AbstractCollection

|

+--java.util.AbstractList

|

+--java.util.ArrayList

すべての実装インタフェース

:

Cloneable, Collection, List,

RandomAccess, Serializable

(6)

抽象

vs

非抽象クラス

Java

は 抽象クラスと呼ばれるインスタンス化できないクラスを持てる

下記の2点を除いて全く同じ

抽象クラス

通常のクラス

インスタンス化

できない

できる

実体は子クラスで

コンストラクタ

実は 持てる

もちろん持てる

抽象メソッド

持てる

持てない

動作定義は子クラスで

抽象クラスの実体オブジェクトは

下位クラスで生成される実体オブジェクトの和集合

(7)
(8)

3

クラス階層の変換に関する注意

Cを「コピー」した 抽象クラス CopyC その子クラス DummyC.メソッド等は全て CopyC から継承 class C { private int x; C(int x) {this.x=x;} double divide(int y) { return (double)x/y; }

public static void main(String[] args) {

C a = new C(3);

System.out.println(a.divide(2)); }

}

abstract class CopyC { private int x; CopyC(int x) {this.x=x;} double divide(int y) { return (double)x/y; } }

class DummyC extends CopyC { DummyC(int x) {super(x);}

public static void main(String[] args) { DummyC a = new DummyC(3);

System.out.println(a.divide(2)); }

(9)

クラス継承(拡大)とオブジェクト参照変数

(abstract) class C

c

extends C

p

(単一継承)

全てのオブジェクトはコンストラクタで生成時に帰属先(そのクラス)情

報を型情報として持つ

下位クラスオブジェクトは上位クラスオブジェクト

下位クラスに帰属するものを、上位クラスの参照変数で指してよい

インタフェース参照変数は実装クラス(とその下位クラス)のオブジェク

トを指して良い

(10)

抽象クラス: 場合分け(集合分割)をすっきり書ける

class Company {

private Employee[] emplist; // 配列要素は FullTime もしくは ParTime オブジェクト

Company(Employee[] emplist) {this.emplist=emplist;} int sumOfSalary() {

int total = 0;

for (int i=0;i<emplist.length;i++) total += emplist[i].salary();

// 自分が持っている salary() メソッドを実行 return total;

}

public static void main(String[] args) {

Employee[] emplist = {new FullTime("yoshiko", 10000), new PartTime("taro", 10,30)}; System.out.println("total = "+(new Company(emplist)).sumOfSalary());

} }

abstract class Employee {//実体オブジェクトは下位で生成する

private String name;

Employee(String name) {this.name=name;}//抽象クラスもコンストラクタを持てる

// 下位クラスのコンストラクタで利用可(デフォルトは自動で適用) // 自クラスオブジェクト生成のためには使えない(抽象性)

abstract int salary(); // 下位クラスオブジェクトに投げればわかります

}

class FullTime extends Employee { private int salary;

FullTime(String name, int salary) {// 上位クラスのコンストラクタを用いた実体生成の例

super(name); this.salary = salary; }

int salary() {return salary;} // FullTime の場合の実装.

// 上位で public ならここでも must be public

}

class PartTime extends Employee {

private int hourPerDay, paymentForHour;

PartTime(String name, int hourPerDay, int paymentForHour) {//PartTime の場合の実装 super(name); this.hourPerDay=hourPerDay; this.paymentForHour=paymentForHour; }

int salary() { return hourPerDay*paymentForHour*20; } }

(11)

インタフェースで書いた場合

class Company {

private Employee[] emplist; int sumOfSalary() {

int total = 0;

for (int i=0;i<emplist.length;i++) total += emplist[i].salary(); return total;

} }

interface HasSalary { int salary(); } //

abstract class Employee implements HasSalary{/* HasSalary 機能を持つことが明示的

ここでの「機能」:何かをする能力.そのためのメソッドを持つ */

public abstract int salary(); /*

下位クラスに共通なメソッドを実装できる場合は,

public int salary() {.... } で実装を定義し,下位クラスはこれを継承して使えばよい. そうでない場合は,この例のように,抽象メソッドとして,メソッド定義を保留し, 下位クラスで(クラス毎の)実装を行う */ // name フィールド等を持たない場合で例示している // これらの属性フィールドを持つ場合は,前のページと同様 }

class FullTime extends Employee { private int salary;

public int salary() {return salary;}//public 等の修飾子も一致しないと上書きされない }

class PartTime extends Employee {

private int hourPerDay, paymentForHour; public int salary() {

return hourPerDay*paymentForHour*20;//5days/week, 4 week }

(12)

single inheritance or multiple inheritance ?

クラス階層:

class C extends P

継承: 下位で未定義の場合,

上位のメソッドの定義を下位でも使う

上位クラスが複数だと定義が衝突する危険性:

親選択ルールを書く? ⇒ コードが複雑化

⇒ 親クラスは一意的

クラス階層は木構造(

user

定義に限定すれば森)

Object:

任意のクラスは

Object

の下位

インタフェース階層:

interface IC extends IP

上位のインタフェースが持つ,メソッドの型を収集

実装クラス: インタフェース階層で収集したすべての抽象メソッドが実行可

能であること.よって,

インタフェースの抽象メソッドの実装を

クラス階層における上位クラスから継承しても良い

(13)

オーバライドと抽象メソッド

自クラスと親クラスで,メソッドの定義が衝突する場合は,最も特殊なものが実行され る(オーバーライド) クラス階層における抽象メソッドは,その実装クラスのメソッドでオーバーライドされ ると考えても良い (抽象メソッドの実装が不備な場合は「オーバーライドできない」との警告) この意味で,抽象クラスは通常のクラスでも代替可で,抽象メソッドは空の定義を持つ メソッドだと考えても良い. ただし,コード上,オーバライドは明示的でない.

abstract class Employee { abstract void hellow(); abstract int salary(); }

class FullTime extends Employee { private int salary;

void hellow() {System.out.println(

"Hellow, I am a full time worker");} int salary() {return salary;}

}

class Employee {

void hellow() {} // do nothing

int salary() {return -1;}// nonsense }

class FullTime extends Employee { private int salary;

void hellow() {System.out.println(

"Hellow, I am a full time worker");} int salary() {return salary;}

}

抽象クラスでない右のEmployeeのhellowとsalary はEmployeeの実体が生成されない限 り,使用されることはなく,下位クラスのhellow とsalary によりオーバライドされる.

(14)

3.1

extends

と データ領域拡大

class P { int x = 1; } class C extends P{ int y = 2; // C(int x, int y) { //明示的 this.x=x; this.y=y; } C(int y) { //明示的.オーバーロード this.y=y; } C() {} // デフォルト void show() { System.out.println("x="+x+", y="+y); }

public static void main(String[] args) { (new C()).show(); (new C(200)).show(); (new C(100,200)).show(); } } /* x=1, y=2 x=1, y=200 x=100, y=200 */ サブクラス C で this.x=x; が実行できる ことからわかるように,親クラスのフ ィールド x も自分のフィールドとして 操作可能 子クラスオブジェクトのデータ領域中に,親 からもらったフィールドも含まれると 考えてよい. 子クラスのコンストラクタを起動すると,子 クラスにいたる top class Object から のパス上のデフォルトコンストラクタ が順次動作し,親からもらった属性の 値は親のデフォルトコンストラクタで 定まる 途中に抽象クラスがある場合も全く同じ.つ まり,抽象クラスもフィールドとコンス トラクタを持て,抽象クラスの下位ク ラスオブジェクト生成時,抽象クラスの デフォルトコンストラクタも実行さる.

(15)

データ領域拡大,その続き

class P {

private int x;

int getX() {return x;} P(int x) {this.x = x;} } class C extends P { private int y; // C(int x, int y) { super(x); this.y=y; } void show() { System.out.println( "x="+getX()+", y="+y); }

public static void main(String[] args) { (new C(100,200)).show(); } } 親のフィールドをカプセル化すると,子 といえども,勝手にアクセスできない 左記のコードでは,ゲッターを子で継承 してアクセス(Read). 左記では,さらに,子のコンストラクタに おいて,親のコンストラクタsuper(int) を使い, int x フィールドに値をセット している 親でセッター

void setX(int x) {this.x=x;}

を定義し,それを継承して使っても良い. C(int x, int y) { this.setX(x);

this.y=y;} ただし,このコンストラクタのみを使う 場合は,親の P(int) は不要 (*) (*) 親がシグネチャ P() でない明示的コンストラクタを持ち,かつ,子でデ フォルトコンストラクタを用いる場合は,親でもデフォルト P() {} を明示的 に宣言しておく必要がある.

(16)

参考:リスト構造と

generic

タイプ

「従業員リスト」のように、要素の並び(リスト)を扱うクラスは標準で提供されている。要 素型は Object で利用する際にはキャストが一般に必要。 java.util.ArrayList (古い実装.新しいバージョンあり) 配列との違いは,capacity オーバーフロー時に,デフォルトもしくは明示的な領域の追 加割り当てを自動で実行してくれる 要素クラスをパラメータ化した generic な ArrayList もOK ArrayList<String> abc 要素クラスが文字列クラスである、ArrayList。 キャストの必要はない(型チェックが可能) (この例の場合,要素に String 以外の型の参照変数を使用するとコンパイルエラー) 動的配列としてインデックスを持つ 削除等でインデックスの再作成など 一定の負荷はかかる 予め要素数が見積もれる場合は、配 列の方が高速で良い LinkedList 双方向リスト 要素の削除に強いがインデックスは持たない import java.util.ArrayList; class Afo {

public static void main(String args[]){

ArrayList<String> slist = new ArrayList<String>(); slist.add("abc");

System.out.println(slist.get(0)); //

ArrayList list = new ArrayList(); // arraylist of object list.add("abc");

(17)

インタフェースを要素型に持つ

generic type

の例

import java.util.ArrayList;

interface Showable { void show(); }

interface SortableObj extends Showable { String name();

int code(); }

abstract class Sorter {

abstract boolean lessThan(SortableObj x, SortableObj y); abstract boolean equals(SortableObj x, SortableObj y); void swap(ArrayList<SortableObj> inputData) {// 使い方の例示

if (lessThan(inputData.get(0),inputData.get(1))) return; SortableObj tmp = inputData.get(1);

inputData.set(1, inputData.get(0)); inputData.set(0, tmp); }

void show(ArrayList<SortableObj> inputData) {

for (int i=0;i<inputData.size();i++) inputData.get(i).show(); System.out.println();

} }

class SortedByCode extends Sorter{

boolean lessThan(SortableObj x, SortableObj y) {return x.code() < y.code();}

boolean equals(SortableObj x, SortableObj y) {return x.code() == x.code();}

}

class SorterByName extends Sorter{

boolean lessThan(SortableObj x, SortableObj y) {return x.name().compareTo(y.name()) <0;} boolean equals(SortableObj x, SortableObj y)

{return x.name().compareTo(x.name()) == 0;} }

//抽象クラスにおける抽象メソッドは public とは限らない.

//下位クラスで実装するときは,上位で宣言したメソッド型のみならず, //アクセス修飾も同じでないとオーバーライドできない

(18)

class St implements SortableObj { private int code;

public int code() {return code;} private String name;

public String name() {return name;}

public void show() {System.out.print(name+"("+code+"),");} St(int i, String name) {code = i;this.name=name;}

// java St code 103 katoh 101 okubo 102 saitoh // java St name 101 okubo 103 katoh 102 saitoh

public static void main(String args[]){

ArrayList<SortableObj> data = new ArrayList<SortableObj>(args.length/2); for (int i=0;i<(args.length-1)/2;i++) {

St st = new St(Integer.parseInt(args[2*i+1]),args[2*i+2]); data.add(st);

};

Sorter sorter;

if (args[0].equals("code"))

sorter = new SortedByCode();

else if (args[0].equals("name")) sorter = new SorterByName();

else sorter = null;// エラーを発生させる

sorter.swap(data); sorter.show(data); }

(19)

クラス階層、インタフェース階層

以下、まとめを兼ねて階層に関する一般論を述べておく クラス階層: C1:下位 (子)、C2:上位 (親)

(abstract) class C1 extends C2

インタフェース階層:

interface B extends B1, ..., Bk (複数可 (*))

実装クラス (**): クラスは既存の上位クラスを拡大し 同時にインタフェースも実装できる

抽象クラスもインタフェースを実装できる (abstract) class C1 extends C2

implements B1, ..., Bk 2つの階層は継承して集めてくる対象が異なる: クラス階層:実行可能なメソッドを継承(収集) インタフェース階層:抽象メソッド(型情報)を継承 実装クラスで実装の義務(クラス階層で収集分も利用可) クラス階層においてパス上での型が同一な複数のメソッド ⇒ 定義の衝突 ⇒ メソッドとオブジェクトの型情報を用いた制御 (*)多重継承をインタフェースは許す点について: インタフェースが事物の様々な側面・機能を、メソッドの型により表現する立場からは、多重継 承できて当然と言える

(**) abstract class A implements B

抽象クラスはインスタンス化できず、インタフェースの具体化として使用されることはない したがって、抽象クラスでの「実装」:

(20)

メソッドの型情報による識別

メソッド τ m (σ1 x1, ..., σn xn) の型 τ m (σ1, ..., σn) クラス階層においてクラスCで実行可能な メソッド: 自クラスC+上位クラスで定義された メソッド 定義の衝突: 型が同一な異なるメソッドが パス上に存在 定義衝突時のオーバーライド: 参照変数が指す実体で決まる! (動的ディスパッチ) 実体が帰属するC以上で定義された最 下位のメソッドを適用 class O3 { void f() { System.out.println("o3"); } } class O2 extends O3 { void f() { System.out.println("o2"); } } class O1 extends O2 { void f() { System.out.println("o1"); }

public static void main(String args[]){ O2 x = new O1(); x.f(); // (*) } } (*) x は O2 の参照変数だが,O1 オブジェクトを参照 よって,O1 オブジェクト が持っている f() が実行 オブジェクトにメッセージをなげ, オブジェクトは自分が持つメソッドを実行 自クラス内では、シグネチャ(メソッド名+引数型)によりメソッドは識別され、シグネチャが異なれ ばオーバーロード 逆に言えば、同一シグネチャのメソッドは不可(出力型が異なっていても) クラス階層・インタフェース階層においては、メソッドは、その型(出力型も含まれる)により識別さ れる。 シグネチャが同じで出力型が異なる場合は、「オーバライドできない」との理由でコンパイルエ

(21)

インタフェース階層とクラス階層(まとめ)

○ クラスCがインタフェースIを実装: I≦J なる 全てのJの全ての抽象メソッド m に対し, 型が同一なメソッドを定義実装する義務をおう ○ Cが抽象クラスの場合は,その実装を下位クラスに 委ねることができる その場合,m は C で abstract method でなければなら ない ○ Cは(抽象,非抽象のいずれの場合も),上位クラス の実装を継承した形で実装しても良い ○ 抽象クラスの抽象メソッドは,インタフェースの実 装の場合は public,そうでない(同一パッケージの場合 は)無修飾で良い (*)上位の抽象クラスAでインタフェースの実装が完了する場合もある。

この場合は、(非抽象)子クラスCでの implements Bは自動的にsatisfied.(So, implementsを つけなくても良いが明示的ではない)

(**)インタフェース実装と同様に、途中の抽象クラスで部分的に実装メソッドを与えてOK。特に、最 後の抽象クラスAが抽象メソッドを持たず、Aより上の抽象メソッドの実装が完了した場合は、A の全ての子クラスCで条件が自動的に満たされる

(22)

総合例題: 自動販売機

/*

トランザクション単位:

硬貨投入から商品選択・出力・つり銭処理まで。キャンセルボタン もつける 硬貨ホルダー: holders.holder(coin.index()): coin 用のホルダー

Holders transaction: 取引が成立までの保管用、ストック用は Holders stock 商品ホルダーは簡単のためつけない。拡張時は Holder クラスを階層化すること 硬貨、商品、ボタン: 抽象クラス Symbol の subclass

実体は interface Symbols の定数として一元管理(修正・追加は Symbols のみ) コマンドライン入力は、各記号の String name 系列。内部的には Symbols の定数で置換 */

abstract class Symbol {// Item 、Coin、Cancel の場合わけ private String name;

String name() {return name;} Symbol(String n) {name = n;}

abstract void message();// 入力時の確認メッセージ }

class Item extends Symbol{ private int price;

int price() {return price;}

Item(String n, int p) {super(n); price=p;}

void message() {System.out.println(" "+name()+" ("+price+" 円) を選択");} }

class Coin extends Symbol{ private int value;

int value() {return value;}

Coin(String name, int v) {super(name); value=v;}

private int index;//holder の配列 holders へのアクセス用 int index() {return index;}

void setIndex(int x) {index = x;}

(23)

interface Symbols {

Coin c10 = new Coin("c10",10), c50 = new Coin("c50",50), c100 = new Coin("c100",100), c500 = new Coin("c500",500); Coin[] coins = {c10,c50,c100,c500};// 額が小さな順に登録

// つり銭計算の都合 Cancel cancel = new Cancel("cancel");

Item orangejuice = new Item("orange juice", 80), bosscoffee = new Item("boss coffee", 120), healthymilk = new Item("healthy milk", 60); Symbol[] symbols = {c10, c50, c100, c500,

cancel,

orangejuice, bosscoffee, healthymilk };

/* 記号に関して必要な処理・機能を SymbolProcess で纏めて実装し他クラスからは 委譲。今回は、利用クラスは VendingMachine ただ一個で、その中で定義してもOK だが、実装クラスとして切り分けた方がより明示的。

*/

ImplementationOfSymbols ios = new ImplementationOfSymbols(); Symbol stringToSymbol(String arg);

void checkCoins(); }

class ImplementationOfSymbols implements Symbols { public Symbol stringToSymbol(String arg) {

for (int j=0; j<symbols.length; j++)

if (symbols[j].name().equals(arg)) return symbols[j]; System.out.println(" *** 不正な入力: "+arg);

return null; }

public void checkCoins() {// Symbols.coins の序数チェック・定義 int max = 0;

for (int i=0;i<coins.length;i++) if (max < coins[i].value()) {

coins[i].setIndex(i); max = coins[i].value(); } else {

(24)

class Holder {/* 各硬貨毎の保管場所で硬貨枚数を管理

stock, transaction: 2つのインスタンス を持つ */ private Coin coin;

Coin coin() {return coin;}

private int nof = 0; //ホルダーが保持する硬貨の枚数 void count() {nof++;} int nof() {return nof;}

void add(int x) {nof += x;} void subtract(int x){nof -= x;}

void reset() {nof=0;} int amount() {return nof * coin.value();} void returnCoin(int x) {

if (x == 0) return;

nof -= x; System.out.println(" "+coin.value()+" 円硬貨を "+x+" 個返却"); }

Holder(Coin coin) {this.coin = coin;} }

class VendingMachine implements Symbols{

public Symbol stringToSymbol(String arg) {return ios.stringToSymbol(arg);} public void checkCoins() {ios.checkCoins();}

//

private Holders stock, transaction; VendingMachine() {

checkCoins(); stock = new Holders(); transaction = new Holders(); }

class Holders {// 内部クラス

private Holder[] holders = new Holder[coins.length]; private Holders() {

for (int i=0;i<coins.length;i++) holders[i] = new Holder(coins[i]); }

private Holder holder(Coin coin) {return holders[coin.index()];} private int totalAmount() {

int sum = 0;

for (int i=0;i<holders.length;i++) sum += holders[i].amount(); return sum;

(25)

private void payBackChange(int amount) { int[] payBackNof = new int[coins.length]; for (int i=coins.length-1;i>=0;i--) {

payBackNof[i] =

Math.min(amount/(coins[i].value()),

stock.holder(coins[i]).nof()+transaction.holder(coins[i]).nof()); amount -= payBackNof[i] * coins[i].value();

}; if (amount > 0) { System.out.println(" つり銭が不足しています"); transaction.returnAllCoin(); return; }; System.out.println(" 商品を出します"); for (int i=0;i<coins.length;i++) {

stock.holder(coins[i]).add(transaction.holder(coins[i]).nof()); transaction.holder(coins[i]).reset();

stock.holder(coins[i]).returnCoin(payBackNof[i]); };

}

void process(Symbol input) { input.message();

if (input instanceof Cancel) { transaction.returnAllCoin();

System.out.println("+++ トランザクション終了"); return; };

if (input instanceof Coin) {

transaction.holder((Coin)input).count(); return; };

if (input instanceof Item) {

int difference = ((Item)input).price()-transaction.totalAmount(); if (difference > 0) {

System.out.println(" "+difference+" 円不足です"); return;//ここではトランザクションは続行させる

};

(26)

int sales() {return stock.totalAmount();} }

class test {

public static void main(String args[]){ VendingMachine vm = new VendingMachine(); Symbol input;

for (int i=0;i<args.length;i++)

if ((input = vm.stringToSymbol(args[i]))!=null) vm.process(input); System.out.println(" ++ 売上高: "+ vm.sales());

} }

//java test c10 c10 c100 "boss coffee" c100 "orange juice" c100 "healthy milk" c1000 c50 c10 c10 "healthy milk" 10 円硬貨を投入 10 円硬貨を投入 100 円硬貨を投入 boss coffee (120 円) を選択 商品を出します ++ トランザクション終了 100 円硬貨を投入 orange juice (80 円) を選択 商品を出します 10 円硬貨を 2 個返却 ++ トランザクション終了 100 円硬貨を投入 healthy milk (60 円) を選択 つり銭が不足しています 100 円硬貨を 1 個返却 ++ トランザクション終了 *** 不正な入力: c1000 50 円硬貨を投入 10 円硬貨を投入 10 円硬貨を投入 healthy milk (60 円) を選択 商品を出します 10 円硬貨を 1 個返却 ++ トランザクション終了 ++ 売上高: 260

instanceof を用いた場合分けについて(void process(Symbol) 中): 一部の文献には 『instanceof を使うのは、クラス階層設計が悪い』 とある。自動販売機では、各 Symbol に対して処理を委ねる形に

(27)

本日の課題

問1

人工的だが、インタフェースの利用とクラス継承を同時に行う右記の プログラム(C.main)の動作を簡潔に説明せよ。 interface A {int f();} class B {

int g(A a) {return 100+a.f();} }

final class C extends B implements A { private int y;

C(int y) {this.y=y;}

public int f() {return y;}

public static void main(String args[]){ C c = new C(100);

System.out.println(c.g(c)); }}

(28)

演習問題2(上位クラスコンスタラクタの使用例)

授業科目には、通常の座学の講義を行う科目と、実験・演習科目に大 別でき、それらの成績集計方法は異なる。通常の座学の科目は、履修者 名簿と期末試験の素点データを保持し、一方、実験・演習科目は、履修 者毎に全部で12回のレポートの評点データを保持していると仮定する. 次頁のプログラムでは,学生毎の,座学および演習科目の成績を,それぞ れ異なるクラス StudentRecordForLecture, StudentRecordForExercise として持たせている.対応する,座学および演習のクラスLecture, Exercise は,それぞれ,StudentRecordForLecture の配列とStudentRecordForExercise の配列を,内部フィールドとして保持する. 2-4-1. 受講学生は履修している科目毎に異なる.この事実は下記のプログラムのコード において,どの箇所で表現されているかを答えよ.また,その理由も述べること. なお,該当箇所は行番号を用いて指示せよ. 2-4-2. 科目のクラス Item は抽象クラスにした.この場合,(A1) 抽象クラスでなければ ならない,(A2) 抽象クラスにしてはいけない,あるいは,(A3) 抽象クラスにして も良い,のいずれであるかを答えよ.その際,抽象クラスの定義や特徴に触れなが ら説明すること. 2-4-3. 設問 (B).Exercise クラスのコンストラクタ Exercise(String, StudentRecordForExercise[]) の定義を完成させよ. また,そのコンストラクタにより,Exercise の実体が StudentRecordForExercise の配列 を保持できる理由を簡潔に述べよ. 2-4-4. 31行∼35行の設問 (A) double avg() はの動作を述べよ.ただし,ホストオ ブジェクトが,Lecture のオブジェクトの場合,およびExcercise のオブジェクト の場合にわけて説明せよ. 2-4-5. 演習,座学を区別せずに,全ての科目平均点の平均を求めるコードを与えよ.新 規のクラスを定義しても良いし,次頁のクラスにメソッドを追加する形で与えても

(29)

1. abstract class StudentRecord {

2. private String name;// 学生名

3. abstract double score();

4. StudentRecord(String name) {this.name=name;} 5. }

6. class StudentRecordForLecture extends StudentRecord{

7. private int score;// 講義科目試験の点数

8. double score() {return score;}

9. StudentRecordForLecture(String name, int score) { 10. super(name); this.score = score;

11. }

12. }

13. class StudentRecordForExercise extends StudentRecord{ 14. private static final int nofTimes = 12;

15. private int[] scores;

16. StudentRecordForExercise(String name, int[] rs) {

17. super(name); scores = rs; // rs.length == 12 等のチェックが必要だが

18. } // 簡単のため省略

19. double score() {

20. double sum = 0.0;

21. for (int i=0;i<nofTimes;i++) sum += scores[i];

22. return sum/nofTimes;

23. }

24. }

25. abstract class Item {

26. private String name;//科目名 27. private StudentRecord[] records;

28. Item(String name, StudentRecord[] records) { 29. this.name = name; this.records = records;

30. }

31. double avg() { // 設問(A)

32. double sum = 0.0;

33. for (int i=0; i<records.length;i++) sum += records[i].score(); 34. return sum/records.length;

35. }

36. }

37. class Lecture extends Item {

38. Lecture(String name, StudentRecordForLecture[] records) {

39. super(name, records);

40. }

41. }

(30)

自由課題(ユーティリティのカスタマイズ)

次頁のプログラムは、多重集合 java ソースである。ただし、多重集合 とは集合だが、要素が何回生起したかのカウンタをつけたもので、例え ば A = {(a,1),(b,3),(c,2)} など。簡単のため、0回しか生起しない ものはAの要素として認めないとする。プログラムは、標準パッケージ 内の class ArrayList を直接 extends している。ここに ArrayList はそ の名称どおり、リスト構造だが、リスト要素への添字(インデックス)を 持つことからリスト要素の「配列」として使える。

ちなみに、自分で責任を持てない(パッケージ中の)メソッドを上書きす るのは危険(良くない継承)であるとする立場もあるので注意すること。

ArrayList の public methods (一部): size(): 要素数をかえす ArrayList(): コンストラクタ: 要素数0のリストを返す add(Object x): x を リストの再後尾に追加する Object 全てのオブジェクトのクラス (クラス階層の最上位に位置する) Object get(int j): 第 j 番目のリスト要素を返す プログラムではインタフェース ObjectWithEquality と上記の ArrayList を用い、多重集合を単純にかつ抽象的に定義している。すなわち、 多重集合コンストラクタ new MultiSet() により空の多重集合を生成 multiSetAdd メソッドを順次適用し、要素を追加・カウント プログラムがどのように動作するかを説明せよ。さらに、動作テスト のための ObjectWithEquality の実装クラスと

その主関数 public static void main(String args[]) を適宜与えよ。 さらに、上位クラスを示す super を用いて、ArrayList の add の定義 を上書きし、MultiSet オブジェクトに(上書きされた)add メソッドを 適用するだけで多重集合が形成されることも確認せよ。

(31)

import java.util.ArrayList; //

class ObjectWithCounter {

private ObjectWithEquality value;

ObjectWithEquality getValue() {return value;} private int counter=1;

void countOne() {counter++;}

int getCounter() {return counter;}

ObjectWithCounter(ObjectWithEquality owe) {value=owe;} }

interface ObjectWithEquality {

boolean equals(ObjectWithEquality x); }

class MultiSet extends ArrayList { private int totalNumber = 0;

int getTotalNumber() {return totalNumber;} void multiSetAdd(ObjectWithEquality owEquality) {

ObjectWithCounter owCounter; if (size()==0 || (owCounter = checkMembership(owEquality))==null ) add(new ObjectWithCounter(owEquality)); else owCounter.countOne(); totalNumber++; } private ObjectWithCounter checkMembership(ObjectWithEquality owEquality) { ObjectWithCounter owCounter;

for (int i=0;i<size();i++)

if (( owCounter = (ObjectWithCounter)get(i) ).

getValue().equals(owEquality)) return owCounter; return null;

} }

参照

関連したドキュメント

This extends the notion of regular variation for Borel measures on the Euclidean space R d to more general metric spaces.. Typically ν is a probability measure but other classes

Thanh and Anh [11] established a strong law of large numbers for blockwise and pairwise m-dependent ran- dom variables which extends the result of Thanh [8] to the arbitrary blocks

- [Lichtenstein, 1912] proves a Harnack inequality for elliptic operators with differentiable coefficients including lower order terms in two dimensions.. - [Feller, 1930] extends

We show (Theorem 4.2) that this interpretation extends to a q-analogue based on the statistic des for alternating Baxter permutations and number of cycles for genus zero per-

What the Vasiliev theory of higher spins does is basically that it extends the gauge algebra so(3, 2) to the higher spin algebra hso(3, 2) (and correspondingly in other dimensions

It turned out that the propositional part of our D- translation uses the same construction as de Paiva’s dialectica category GC and we show how our D-translation extends GC to

We show that the sizes of the (rescaled) components converge to the excursion lengths of an inhomogeneous Brownian motion, which extends results of Aldous [ 1 ] for the

The class of SWKA Banach spaces extends the known class of strongly weakly compactly generated (SWCG) Banach spaces (and their subspaces) and it is related to that in the same way