Back To Main

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ ◆Javaで珈琲ブレイク vol.008 ◆
┃……………………………………………………………………………………………
┃ [不定期] まぐまぐ ID=0000088576 Melma! ID=m00061296
┃……………………………………………………………………………………………
┃ 今回からご覧になる方は、バックナンバーもご活用下さい
┃ http://www.melma.com/mag/96/m00061296/
┃ http://backno.mag2.com/reader/Back?id=0000088576
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆目次◆

■今後の予定
■前回の解答
■BankAccount.java
■BankAccountTest.java
■finalキーワード
■FinalKeyword.java
■Exceptionがダメな理由
■ExceptionsDemo.java


皆さん、こんにちは。

Mr.Hackです。五月病大丈夫ですか?4月に新学期・新年度がはじまって、胸をふくらましていた皆さん、何かがちがう・・・なんて、悩んでいませんか?また、あれもやろう、これもやろうと、おもっていたのに、・・・なんてことになっていませんよね?まだ、4月から2ヶ月たってないんです。がんばっていきましょう。昨日より、今日。今日より明日。一歩一歩進んでいきたいですね。

■今後の予定
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

皆さんにさっそく中だるみがきそうな時期ですので、今後の予定をざっとお話ししようとおもいます。

今回は、前回の宿題の解答をみまして、もう一度、チェック有り・なし
Excpetionnの確認と、Excpetionの使用が好ましくない理由を確認しましょう。次回は、Javadocコマンドを使って、Java documentationの作り方を勉強します。そしてその次の週は、いよいよ、ユーザーインターフェースの作成です。コマンド上で、銀行の預金メニューが現れて、預金をしたり、引き出しをしたり出来るようにします。これは、結構クールですよ。やっとユーザーインターフェイスという、ユーザーからするとソフトウェアらしい部分に入りますからね。いろいろと、コマンド入力しながら、遊びましょう。その後は、もう一度、BankAccountクラスにもどって、Encapsulation(データのカプセル化、隠蔽化)を勉強します。

そして、実際に、銀行のお金の出し入れには必至な、セキュリティーを設定します。あるIDに対するパスワードが一致したら、ログイン出来るようにします。ちょうど銀行のATMでカード(ID)をいれてパスワードを打つような感じです。

それが、おわったら、いよいよ、Borland社のフリーソフトJBuilder6 Personal版を使っていこうと思います。皆さんは、すでにCPadから多大な恩恵を受けていることが納得して頂けたと思います。Java SDKをインストールしたところに環境変数を設定しなくてもいいし、毎回コマンドを打たなくても、ボタン一つで実行出来ました。しかし、JBuilderを使うともっと便利な機能も使えたりします。それをせっかくですから、味わって見ましょう。ただ、CPadはCPadで簡単にコンパイル実行出来るという何よりも代え難い長所があるので、その長所を生かせるときには、CPadを使っていきましょう。

ここまでが、短期的なスケジュールです。

中期的スケジュールも聞きたいですか?

中期的には、銀行プログラムから、アドレス帳に入っていきます。このプロジェクトをとおして、継承を勉強します。LinkList等のアルゴリズムを勉強します。ファイルへのデータ保存を勉強します。ここまでで、簡単なWeb Applicationに耐えられる考え方を学びます。

そして、おまけに、長期的スケジュールを。皆さんがコンピューターサイエンス学科の3年生ぐらいまでの基礎的な知識がついた頃で、いよいよJSP-Servletに入っていきます。これは、このお題だけで一つのメールマガジンが書けるほど、勉強することがいっぱいあります。そして、日本のコンピューターサイエンス学科に通っている学生に負けないぐらいになってもらいます。日本のコンピューターサイエンスの学生の方は、Java珈琲ブレイクを購読していないクラスメイトに負けないようになってもらいます。MVC paradigm(Model-View-Controllerパラダイム)もふれましょう。MVCってなに?長期的に待てない、今すぐ知りたい!という方は、ちょっとだけ、のぞき見をどうぞ。

日本語翻訳版
http://www-6.ibm.com/jp/developerworks/java/010824/j_j-struts.html

原文(英語)
http://www-106.ibm.com/developerworks/library/j-struts/?dwzone=java

これを聞いて、皆さんはやる気が出てきましたか?それとも、こんなにまだ習うことがあるのと、思いましたか?そう思った人は、上の日本語翻訳版をクリックしてみてください。今は分からなくても、いずれ、これが分かるように勉強していこうと思っています。このMVCパラダイムはかなりクールですよ。perlやASPでは実現出来ない美学ですね。JSPページにはJavaコードは全くと言っていいぐらい出現しなくなります。Servletには全くHTMLコードは出現しなくなります。そのため、WebデザイナーはJavaプログラムに悩まされることなくデザインに集中できますし、一方で、皆さんのようなプログラマはWebデザインを気にすることなくプログラムに集中できるのです。何とも、美しい構図ですね。こんなことも、皆さんにJavaの基礎体力が出来てきたら、紹介していこうと思っています。楽しみにしていてください。

それでは、前回の解答です。

■前回の解答
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Question 7 の答え

■BankAccount.java
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
/**
* バンク預金口座クラス。顧客の口座の出し入れ、残高
* 照会をおこなう(通貨単位:円)。預入引き落とし時、
* Precondition(条件)をチェックし、条件に合わない場合は、
* 例外を発生させる。残高をゼロとするデフォルトのコンストラクタ、
* 預金額をセットできるコンストラクタがある。また、一度に引き出せる上限は
* 2万円、一度に預金できる上限は10万円としてある。
*
* 例:
*
* <PRE>
* BankAccount account = new BankAccount(2000);
* try {
* account.deposit(1000);
* account.withdraw(500);
* }
* catch (Exception e) {
* Sytem.out.println("例外が発生しました:" + e.getMessage());
* }
* double currentBalance = account.getBalance();
* </PRE>
*
* 2000円を口座開設時預金し、1000円を預金後、600円を引き出して、現在の残高400円
* を<CODE>currentBalance</CODE>に保持する。
*
* @version 1.0.1
* @since 1.0.0
* @author Mr.Hack
*/
public class BankAccount {

/**
* 口座の残高
*/
private double balance;
/**
* 一度に預け入れる上限額
*/
private final double MAX_DEPOSIT_AMOUNT = 100000.0;
/**
* 一度に引き落とせる上限額
*/
private final double MAX_WITHDRAW_AMOUNT = 20000.0;

/**
* 残高をゼロにセットするデフォルトコンストラクタ。初期化時、
* 残高をゼロにセットする。
*
* @author Mr.Hack
* @since 1.0.0
*/
public BankAccount() {
this.balance = 0;
}

/**
* <code>amount</code>分を残高としてセットするコンストラクタ。初期化時(新しい口座作成時)、
* 預金額を残高としてセットする。
*
* @since 1.0.0
* @param amount 口座に預け入れる金額(預金)
* @exception IllegalArgumentException <code>amount</code>がゼロかマイナスの場合、定められた一度に預金できる
* 金額を上回った場合、例外発生。
*/
public BankAccount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("入力した金額は0かマイナスです。amount: " + amount);
}
if (amount > MAX_DEPOSIT_AMOUNT) {
throw new IllegalArgumentException("一度に入金できる額の上限は" + MAX_DEPOSIT_AMOUNT + "です");
}
this.balance = amount;
}

/**
* <code>amount</code>分を預金として口座に預ける。<code>amount</code>がゼロかマイナスの時、一度に預ける
* 上限金額を上回った時、例外を発生させる。
*
*
* @since 1.0.0
* @param amount 口座に預け入れる金額(預金)
* @exception IllegalArgumentException <code>amount</code>がゼロかマイナスの場合、定められた一度に預金できる
* 金額を上回った場合、例外発生。
*/
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("入力した金額は0かマイナスです。amount: " + amount);
}
if (amount > MAX_DEPOSIT_AMOUNT) {
throw new IllegalArgumentException("一度に入金できる額の上限は" + MAX_DEPOSIT_AMOUNT + "です");
}
this.balance += amount;
}

/**
* <code>amount</code>分の金額を引き出す。<code>amount</code>がゼロかマイナスの時、引き落とし金額が残高を上回った時、
* あるいは、一度に引き出せる上限金額を上回った時、例外発生。
*
* @since 1.0.0
* @param amount 引き出す金額
* @exception IllegalArgumentException <code>amount</code>がゼロかマイナスの場合、引き出し額が残高を上回った場合、
* または、一度に引き出せる金額を上回った場合、例外発生
*/
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("入力した金額は0かマイナスです。amount: " + amount);
}
if (balance < amount) {
throw new IllegalArgumentException("残高を超えています");
}
if (amount > MAX_WITHDRAW_AMOUNT) {
throw new IllegalArgumentException("一度に引き落としできる額の上限は" + MAX_WITHDRAW_AMOUNT + "です");
}
this.balance -= amount;
}

/**
* 現在の残高を戻り値として返す。
*
* @since 1.0.1
* @return balance 口座にある現在の残高
*/
public double getBalance() {
return this.balance;
}


/**
* 現在の残高を戻り値として返す。
*
* @deprecated メソッド名が不適切なため、Version 1.0.1から{@link #getBalance}に入れ替わり。
* @see #getBalance
* @since 1.0.0
* @return balance 口座にある現在の残高
*/
public double getBlance() {
return this.balance;
}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━

■BankAccountTest.java
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
/**
* BankAccountをテストするクラス。BankAccountのメンバメソッドdeposit(),
* withdraw(), getBlance(), getBalance()をそれぞれテストする。
*
* @version 1.0815
* @since 1.0511
* @author Mr.Hack
*/
public class BankAccountTest {
/**
* deposit()、withdraw(), getBlance()メソッドを
* それぞれテストする。
* <PRE>
* deposit()
* 1.マイナス引数の場合
* 2.ゼロ引数の場合
* 3.100001(上限金額超過)の場合
*
* withdraw()
* 1.マイナス引数の場合
* 2.ゼロ引数の場合
* 3.20001(上限金額超過)の場合
*
* getBlance()
* 1.残高1000円、期待値1000円の場合
* 2.残高399円、期待値3999円の場合
*
* getBlance()
* 1.残高1000円、期待値1000円の場合
* 2.残高399円、期待値3999円の場合
* </PRE>
*
* @since 1.0511
*/
public static void main(String[] args) {

BankAccount account = new BankAccount();

// deposit()メソッドテスト
System.out.println("deposit()テスト");
/** マイナス引数テスト 引数-1で例外を発生させる */
try {
account.deposit(-1);
System.out.println("マイナス引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("マイナス引数テスト成功。例外:" + e.getMessage());
}
/** ゼロ引数テスト 引数0で例外を発生させる */
try {
account.deposit(0);
System.out.println("ゼロ引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("ゼロ引数テスト成功。例外:" + e.getMessage());
}
/** 上限金額超過引数テスト 引数100001で例外を発生させる */
try {
account.deposit(100001);
System.out.println("上限金額超過引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("上限金額超過引数テスト成功。例外:" + e.getMessage());
}

// withdraw()テスト
System.out.println("");
System.out.println("withdraw()テスト");
/** マイナス引数テスト 引数-1で例外を発生させる */
try {
account.withdraw(-1);
System.out.println("マイナス引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("マイナス引数テスト成功。例外:" + e.getMessage());
}
/** ゼロ引数テスト 引数0で例外を発生させる */
try {
account.withdraw(0);
System.out.println("ゼロ引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("ゼロ引数テスト成功。例外:" + e.getMessage());
}

/** 上限金額超過引数テスト 引数20001で例外を発生させる */
try {
account.deposit(50000);
account.withdraw(20001);
System.out.println("上限金額超過引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("上限金額超過引数テスト成功。例外:" + e.getMessage());
}
/** 残高超過引数テスト 1000円入金後、引数3000で例外を発生させる */
try {
account.deposit(1000);
account.withdraw(3000);
System.out.println("残高超過引数テスト失敗。例外発生できず");
}
catch (IllegalArgumentException e) {
System.out.println("残高超過引数テスト成功。例外:" + e.getMessage());
}


//getBlance()テスト
System.out.println("");
System.out.println("getBlance()テスト");
/** 残高1000円テスト 期待値1000円 */
account = new BankAccount(); //accountをリセット
double expectedAmount = 1000;
account.deposit(expectedAmount);
if (account.getBlance() == expectedAmount) {
System.out.println(expectedAmount + "円入金後テスト成功。Balance : " +
account.getBlance());
}
else {
System.out.println(expectedAmount + "円入金後テスト失敗。Expected Balance :" +
expectedAmount + " Actual Balance ; " + account.getBlance());
}

/** 残高399円テスト 期待値399円 */
account = new BankAccount(); //accountをリセット
expectedAmount = 399;
account.deposit(1000);
account.withdraw(601);

if (account.getBlance() == expectedAmount) {
System.out.println(expectedAmount + "円入金後テスト成功。Balance : " +
account.getBlance());
}
else {
System.out.println(expectedAmount + "円入金後テスト失敗。Expected Balance : " +
expectedAmount + " Actual Balance ; " + account.getBlance());
}

//getBalance()テスト
System.out.println("");
System.out.println("getBalance()テスト");
/** 残高1000円テスト 期待値1000円 */
account = new BankAccount(); //accountをリセット
expectedAmount = 1000;
account.deposit(expectedAmount);
if (account.getBalance() == expectedAmount) {
System.out.println(expectedAmount + "円入金後テスト成功。Balance : " +
account.getBalance());
}
else {
System.out.println(expectedAmount + "円入金後テスト失敗。Expected Balance :" +
expectedAmount + " Actual Balance ; " + account.getBalance());
}

/** 残高399円テスト 期待値399円 */
account = new BankAccount(); //accountをリセット
expectedAmount = 399;
account.deposit(1000);
account.withdraw(601);

if (account.getBalance() == expectedAmount) {
System.out.println(expectedAmount + "円入金後テスト成功。Balance : " +
account.getBalance());
}
else {
System.out.println(expectedAmount + "円入金後テスト失敗。Expected Balance : " +
expectedAmount + " Actual Balance ; " + account.getBalance());
}

}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━

Question 7-1

a) スローする例外は全て、IllegalArgumentExceptionに変更しなさい。

これは、スローされるExceptionクラスの代わりに、Exceptionのサブクラスである、IllegalArgumentExceptionに変えるだけですね。コピー&ペーストするだけなので問題ないでしょう。また、BankAccountクラスのメソッド定義のところは、チェックなしExceptionなので、throwsキーワードを使う必要がなくなりますね。

ここで、前回BankAccountクラスで説明していなかった、finalというキーワードについて勉強しましょう。BankAccountでは

private final double MAX_DEPOSIT_AMOUNT = 100000.0;

と、finalが使われています。privateというキーワードは後ほどEncapsulation(データーの隠蔽化)で詳しく説明しますが、とりあえずは、BankAccountのクラスの中からしかその変数にアクセス出来ない、と考えてください。そのため、BankAccountクラスの外にあるBankAccountTestクラスからはMAX_DEPOSIT_AMOUNTにアクセスできません。

■finalキーワード
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
finalというキーワードは、『設定・変更不可能な』と言うことを意味します。finalがついた変数は、変数宣言または、その後の代入(割り当て)で一回だけ、初期値を設定できます。それが最初で『最後』の設定なので、finalというキーワードを使うと覚えましょう。finalで指定される変数(厳密には、変更が出来ないので変数(variable)ではなく、値(value))はJavaの慣習で全て大文字、かつ単語の間はアンダースコア('_')を使います。つまり、アンダースコア付きの全て大文字の変数を見れば、設定変更が不可能な値だと分かるのです。以下がサンプルプログラムです。

■FinalKeyword.java
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
/**
* finalキーワードのデモ。必ずしも変数宣言で代入・割り当てをする必要なし。
* しかしながら、最初で最後で1度だけ代入・割り当て可能。
*
* @author Mr.Hack
*/
class FinalKeyword {
public static void main(String[] args) {
final String MY_NAME;
MY_NAME = "Mr.Hackです";
// finalのため変更不可。コンパイルするためには、
// //で以下の行をコメントにする
MY_NAME = "Miss,Hackです";
System.out.println(MY_NAME);
}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━

ところで、なぜ、MAX_DEPOSIT_AMOUNT = 100000.0とおいたのでしょうか?それは、コードのメインテナンス(維持)のしやすさにあります。もし、各メソッドにそのまま、100000.0という値を使っていた時に、ふと、50000.0に変更したくなったらどうしますか?一つづつ50000.0に変更していきますか?

それよりも、MAX_DEPOSIT_AMOUNT = 100000.0とおくことによって、その100000.0を50000.0に変えるだけですんでしまうのです。何とも便利ですね。

b) パラメータ付きのコンストラクタもdeposit()と同じように条件によって、
例外を発生するようにしなさい。

これは、deposit()メソッドと同じことをします。コンストラクターも同じようにthrow/throwsキーワードを使って例外を発生させることが出来ます。


public BankAccount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("入力した金額は0かマイナスです。amount: " +
amount);
}
if (amount > MAX_DEPOSIT_AMOUNT) {
throw new IllegalArgumentException("一度に入金できる額の上限は" +
MAX_DEPOSIT_AMOUNT + "です");
}
this.balance = amount;
}

コンパイル時にチェックなしのRuntimeExceptionのサブクラスであるIllegalArgumentExceptionは、メソッドの定義にthrowsキーワードを使う必要はありませんが、このクラスを使うクライアント(client)のために、Java documentationとしてメソッドの説明に『必ず』@exceptionタグを記述してくださいね。そうしないと使う側のクライアントはRuntimeに例外が発生するかどうかJava documentationからは分かりません。

もう一度クライアントという単語を確認しておきましょう。あるクラス(API)を使う(側の)クラスをクライアントといいましたね。ここでは、”あるクラス”とは、BankAccountクラスで、”クライアント”とは、BankAccountTestクラスでしたね。

今回の宿題では、コンパイル時にチェックがあるExceptionクラスを使う代わりに、コンパイル時にチェックしないIllegalArgumentExceptionクラスを使いました。これは、BankAccountクラスを使うクライアントの負担を軽減してくれます。なぜなら、クライアントは、初期化時適切な値(ゼロかマイナスでない値)を使うことさえ守っていれば、わざわざtry/catch構文を使わなくてすむからです。

BankAccountのコンストラクタのスロー宣言にExceptionクラスを使った場合は、クライアントは、

//必ずtry/catch構文を使わなくてはならない。さもないと、コンパイルエラー
try {
BankAccount account = new BankAccount(1000);
}
catch (Exception e) {
}

としなければ、なりません。一方で、BankAccountのコンストラクタにRuntimeExceptionのサブクラスであるIllegalArgumentExceptionを使った場合は、そのまま、

//try/catch構文なしでも可
BankAccount account = new BankAccount(1000);

と初期化出来ます。明らかに、後者の方が、クライアントの負担は軽いですね。しかしながら、これは、クライアントがアーギュメント(実引数、上記の場合は、1000)にゼロかマイナスを渡さないという前提の上にtry/catch構文を省くことが出来るのです。BankAccount君はクライアント君に言います。クライアント君、初期化するときに、あなたの負担を軽減してあげるよ。ただし、僕に値を渡すときは、必ず、ゼロかマイナスではない、値にしてね、と。

逆に、積極的に、初期化するときにゼロ、マイナスの値も含めて渡すことも出来ます。そのときは、クライアント側は、RutimeExceptionがでることを念頭にtry/catch構文で身構えるのです。こうなると、Exceptionクラス、またはそのサブクラスを使ったときと、コンパイル時チェックされないという点を除いて変わらなくなりますね。

もうひとつ、2)で変更しないといけないところがあります。それは、パラメータなしのデフォルトコンストラクタです。デフォルトでは、

BankAccount() {
this(0);
}

となって、thisでパラメータ有りのコンストラクタを、0を渡して、呼んでいますね。これだと実質、

BankAccount account = new BankAccount(0);

と同じことをしていることになってしまいますね。ゼロがパラメータとして渡されると、IllegalArgumentExceptionをスローしてしまうので、よろしくありませんね。そのため、

BankAccount() {
this.balance = 0;
}

と変更しましょう。こちらのthisはインスタンスを表しましたね。

Question 7-2

a) main()メソッドでthrowsキーワードは使ってはならない。

ExceptionをチェックなしのIllegalArgumentExceptionに変えたので、main()メソッドもthrowsキーワードを使う必要がなくなりましたね。main()メソッドで誰も実行時(ランタイム)にスローされたIllegalArgumentExceptionをキャッチしてくれない場合は、Javaインタープリタが実行時に伝家の宝刀としてキャッチしてくれます。伝家の宝刀と言っているぐらいですから、ここでキャッチするのはよろしくありませんね。華氏から摂氏への変換プログラムを思い出してください。何も入力せずにEnterキーを押したときに訳の分からない文が表示されましたね。プログラムを全く知らないユーザーはあれを見たとき、何が起こったか分かりません。実行時の例外(Runtime Exception)でも、最低ユーザーが分かる言葉(たとえば、マイナスを入力したときに『マイナスは不適切な値です』等)を表示しなければなりませんね。

b) try/catch構文では、Throwable、Exception、RuntimeExceptionクラスをつ かってはならない。

BankAccountTestクラスのdeposit()メソッド、withdraw()メソッドのテストには、Throwable、Exception、RuntimeExceptionを使う代わりに、IllegalArgumentExceptionをtry/catch構文で使います。でも、なぜあえて、IllegalArgumentExceptionを使うのでしょう?例外・エラーのスーパークラスである、Throwableを使えば全ての例外・エラーをキャッチ出来るように見えます。現に、Throwableを使えば、そのサブクラスである、Error、Exceptionはもちろん、RuntimeExceptionまでキャッチすることが出来ます。Exceptionを使えば、ExceptionのサブクラスとRuntimeExceptionとそれのサブクラスをキャッチすることが出来ます。では、なぜ、Throwable、Exceptionをtry/catch構文で使うのはよろしくないのでしょうか?

■Exceptionがダメな理由
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
何でもキャッチ出来るとは、裏を返せば、個別に、各々の例外を識別出来ないことを意味します。どういうことかと言いますと、もし、メソッドが、IOExceptionとNumberFormatException(IllegalArgumentExceptionのサブクラス)をスローする場合を考えてみてください。例えば、ExceptionsDemoクラスにあるこんなメソッドです。

public double getDouble() throws IOException,NumberFormatException{

BufferedReader buffer = new BufferedReader(
new InputStreamReader(System.in));
// readLine()がIOExceptionをスロー
String line = buffer.readLine();
// parseDouble()がNumberFormatExceptionをスロー
return Double.parseDouble(line);
}

もう皆さんにはおなじみですね。BufferedReaderのインスタンス、bufferはバッファーにキャラクタストリームを蓄えていますね。そして、readLineで一行分を読み込みます。その読み込んだものをDoubleクラスのstaticメソッドparseDoubleでdouble型に変換してあげて、その値を戻り値として返すメソッドです。メソッド定義のところで、NumberFormatExceptionは、RuntimeException(チェックなしException)のため、宣言する必要はありませんが、わかりやすいように示しました。

もし、このgetDouble()を使う側のクライアントがtry/catch構文でExceptionを使った場合はどうでしょう?

ExceptionsDemo demo = new ExceptionsDemo();
try {
double doubleNumber = demo.getDouble();
}
catch (Exception e) {
// IOExceptionなのか、NumberFormatExceptionなのか分からず
}

RuntimeExceptionクラスを継承しているNumberFormatExceptionは使う側の、クライアント(ここではソフトを実際に使うユーザー)が正しく値を渡さなかった(入力)しなかったためにスローされるのでしたね。その場合は、十分にプログラムはリカバリー可能(ユーザーにもう一度入力を促すことが可能)なので、その場合は、loopなどを使いもう一度、getDoube()メソッドを使いたいですね。それ以外は、IOExceptionが起こった場合は、プログラムを直ちに終了させたいでしょう。しかしながら、上記は、IOExceptionなのか、NumberFormatExceptionなのか分からないため、プログラムの流れを分岐することが出来ません。もし、各々の例外を個別にキャッチできれば、この各々に対して個別にプログラム処理ができます。

while (true) {
try {
double doubleNumber = demo.getDouble();
//NumberFormatException等の例外が発生しない場合だけ、
//while loopを抜ける
break;
}
catch (NumberFormatException e) {
System.out.print("数字をもう一度入力してください:");
}
catch (IOException e) {
System.out.println("深刻な問題が発生したため終了します。");
//IOExceptionが起こるとリカバリーは難しいので終了させる
return;
}
}

これが、try/catch構文にExcpetionクラスでは、Questionで上司が一般的すぎると言った所以です。以下が、getDouble()メソッドとgetDouble()を使うmain()メソッドを同じクラスに入れたサンプルで、ユーザーがちゃんと数字を入れるとwhile loopを抜けて、『入力ありがとうございました』と表示します。

■ExceptionsDemo.java
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 発生した例外(Excpeiton)によってプログラム処理を変えるプログラム。
* 一般的なスーパークラスであるExceptionを使った場合このような処理が出来ない
* ことに注目。
*
* @author Mr.Hack
*/
public class ExceptionsDemo {
/**
* ユーザーが入力した値をdouble型の戻り値として返す。
*
* @since 1.0520
* @return ユーザーが入力したdouble型の数値
* @exception IOException IOエラーが発生した場合
* @exception NumberFormatException ユーザーが数字ではない値を入力した場合
*/
public double getDouble() throws IOException,NumberFormatException{
BufferedReader buffer = new BufferedReader(
new InputStreamReader(System.in));
// readLine()がIOExceptionをスロー
String line = buffer.readLine();
// parseDouble()がNumberFormatExceptionをスロー
return Double.parseDouble(line);
}

/**
* ユーザーが入力した値が数値であれば、お礼の言葉を言いプログラム終了。
* さもなければ、数値を入力するまで続行。IOExceptionが起こったときは
* 例外が起こったことをユーザーに通知し、終了。
*
* @param args コマンドライン引数。使用せず
*/
public static void main(String[] args) {

ExceptionsDemo demo = new ExceptionsDemo();

double doubleNumber = 0;
System.out.print("数字を入力してください : ");
while (true) {
try {
doubleNumber = demo.getDouble();
//NumberFormatException等の例外が発生しない場合だけ
//(ユーザーが数値を入力した場合だけ)、
//while loopを抜ける
break;
}
catch (NumberFormatException e) {
System.out.print("数字をもう一度入力してください:");
}
catch (IOException e) {
System.out.println("深刻な問題が発生したため終了します。");
//IOExceptionが起こるとリカバリーは難しいので終了させる
return;
}
}

System.out.println("入力ありがとうございました。" +
"あなたの入力した値 : " + doubleNumber);
}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━

今回は、予定を少し変更して、前回の宿題を中心に、チェック有り・なしExceptionの確認とtry/catch構文を使うときにExceptionをさけた方がいい理由を説明しました。今回説明できなかったJava documentationは来週の説明に回したいと思います。Java docはこれから、皆さんがJavaでプログラムをしていく上で決してさけてもらいたくないものなので、一回分を使ってちゃんと説明しようと思います。

それでは、みなさん、来週まで、Have a nice day!

Mr.Hack

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆Javaで珈琲ブレイク◆
────────────────────────────────────
皆様からの激励・批判のメールをお待ちしております。
【発行者】 Mr.Hack - javacafebreak@hotmail.com
【掲示板】 http://fweb.midi.co.jp/~romanhikou/cafe_entrance.html
※質問は上記の掲示板からどうぞ。
【サイト】 http://mrhack.hoops.ne.jp/
【発行数】 まぐまぐ[856] Melma[134]
【解除】http://mrhack.hoops.ne.jp/
※解除は上記のサイトからいつでもできます。
────────────────────────────────────
(c)2002 MR.HACK ALL RIGHTS RESERVED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


© 2002 MR.HACK ALL RIGHTS RESERVED