Back To Main

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

◆目次◆

■メインメニュー
■switch文
■isContinued()メソッド


皆さんこんにちは。

Mr.Hackです。お元気ですか?

前回のコンソールベースのユーザーインターフェイスはどうでしたか?楽しめましたか?プログラムが起動してから、一連に預金・引き出し・残高参照としているため、なんとも使いづらいですね。それも、預金しないときはゼロを入力しなければなりませんでした。


■メインメニュー
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

これをさける一つの方法として、メインメニューを作り、その一つ一つの項目を選ぶと、その項目のロジックを実行させるようにすれば、より使いやすくなりそうです。例えば、メインメニューを

┌──────────────────────────────────┐
_________________________________________

[1] 預金をする
[2] 引き出しをする
[3] 残高を参照する
[4] 取引を終了する
_________________________________________

└──────────────────────────────────┘

のように表示して、1を選ぶと預金のロジックをするようにするのです。そして、そのロジックが終了すると、また上記のメインメニューに戻ります。こうすると、一連に預金・引き出し・残高参照する前回のユーザーインターフェイスより操作しやすくなりそうですね。

メインメニューが表示された後に、ユーザーに入力を促します。例えば、こんな感じです。

┌──────────────────────────────────┐

ご希望のお取引番号をお選び下さい :

└──────────────────────────────────┘

ユーザーが入力した値をコンソールから得るには、BufferedReaderクラスのインスタンスを使いました。

┌──────────────────────────────────┐

BufferedReader buffer = new BufferedReader(
new InputStreamReader(System.in));
String choice = buffer.readLine();

└──────────────────────────────────┘

コンソールから、String型のchoiceを得ることが出来ましたが、このchoiceを元にどうやって、各項目のロジックを実行するようにすればいいのでしょう?各ロジックは、if文をつかったり、switch文を使ったりして分岐できます。
たとえば、if文を使うなら、

┌──────────────────────────────────┐

if (choice.equals("1")) {
// 預金ロジック
}
else if (choice.equals("2") {
// 引き出しロジック
}
else if (choice.equals("3") {
// 残高参照ロジック
}
else if (choice.equals("4") {
// プログラム終了
}
else {
//上記の条件以外の場合
}
...

└──────────────────────────────────┘

とできます。では、switch文を使う場合はどうすればいいでしょうか?

■switch文
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

switch文を使うと、

┌──────────────────────────────────┐

switch (choice) {
case '1':
// 預金ロジック
break;
case '2':
// 引き出しロジック
break;
case '3':
// 残高参照ロジック
break;
case '4':
// プログラム終了
break;
default:
// 上記に当てはまらない場合
}

└──────────────────────────────────┘

というように書けます。switch文を使ってもif文と全く同じことをすることが出来ます。ただし、switch文は変数choiceとしてString型がつかえません。使えるのは、byte、char、short、int型だけです。つまり、buffer.readLine()はString型の値を返しますから、何らかの方法でString型→char型と変換してあげないといけません。その変換にはどのメソッドを使いましたか?そうです。String#charAt()メソッドですね。charAt(int Index)メソッドはString型(実はchar[]型をラップしたもの)のindexのポジションにあるchar型のキャラクタを得ることが出来ますので、

┌──────────────────────────────────┐
char choice = buffer.readLine().charAt(0);
└──────────────────────────────────┘

で一文字目の値を得ることができます。char型に変換すれば、switch文として使うことができますね。ところで、buffer.readLine().charAt(0)の"."(ドット)はどう解釈するのでしたっけ?

┌──────────────────────────────────┐
char choice = (buffer.realLine()).charAt(0);
└──────────────────────────────────┘

と、buffer.readline() をまず読みます。これが String 型を返しますね。その String 型のインスタンスに"."で charAt() メソッドを適用すると char 型であchoiceを得ることができます。

┌──────────────────────────────────┐
String line = buffer.readLine();
char choice = line.charAt(0);
└──────────────────────────────────┘

と同じ意味です。

話を switch 文に戻します。switch 文で int 型が使えると書きましたが、僕は char型を使った方がいいとおもいます。というのは、case に

┌──────────────────────────────────┐
case 1: //1は''(シングルクォーテーション)がないのでint型
└──────────────────────────────────┘

とは書けますが、

┌──────────────────────────────────┐
//'a'と違い、ここではaは変数と解釈され、
//aが定義されていないとコンパイルエラー
case a:
└──────────────────────────────────┘

とは書けないのです。つまり、switchでaと言う文字を判別したいときは、

┌──────────────────────────────────┐
case 97: //97はchar型で'a'
└──────────────────────────────────┘

とするしかありません。'a'がint型で97と覚えている人は少ないと思いますので、switch文にはchar型を使った方が使いやすいと思います。

それから、switch文で注意しないといけないのは、条件分岐としてbreak(switch文を抜け出す)やreturn(switchを含むメソッドを抜け出す)、throw(try/catch文があるところまで抜ける)を使うことです。もしそれらがない場合は、全て上から順に実行されてしまいます。例えば、

┌──────────────────────────────────┐

switch (choice) {
case '1':
// 預金ロジック
case '2':
// 引き出しロジック
case '3':
// 残高参照ロジック
case '4':
// プログラム終了
default:
// 上記に当てはまらない場合
}

└──────────────────────────────────┘

で choice が '1'なら、預金・引き出し・残高・プログラム終了・上記に当てはまらない場合、まで全て実行されてしまいます。この点を注意しておいて下さい。

あとは、前回の預金ロジック、引き出しロジック、残高参照ロジック、プログラム終了ロジックを各 case 以下に移せばいいと思います。預金ロジック、引き出しロジックでは、今回では、『0』は預金・引き出しをしないということを意味しないので、0 もそのまま、deposit()、withdraw() に渡して良いでしょう。ただし、その場合は、IllegalArgumentException が発生しますので、例外処理もします。

プログラム終了のロジックですが、前回はユーザーが'q'か'Q'を入力したときだけプログラムを終了するようにしました。今回は、メインメニューで4を選ぶと終了出来ますが、もし誤って4を押してしまったらどうしましょう。4を押すこと自体が意思表示だから、間違えることがないのでしょうか?テンキーではないキーボード上の数字は1から順に並んでいますね。当然ですが、4の右隣は3です。残高を参照しようと思って、3を押したかと思ったら4を押してそのままリターンキーを押してしまう場合もあるかもしれません。それを防止するために、本当に4の『プログラムを終了する』のかどうかをもう一度ユーザーに問い合わせてみましょう。

そのユーザーに取引を続けるかどうかを問い合わせるロジックをisContinued()として分離します。このメソッドがtrue返せば、取引続行、falseを返せば取引終了(プログラム終了)そして、そのboolean型をnextTransactionというboolean型のフラグにセットしてこのフラグがfalseのあいだは取引を続行するようにしてみましょう。

┌──────────────────────────────────┐

boolean nextTransaction = true;
while(nextTransaction) {
//メインメニュー表示
char choice = //コンソールからchar型の値、choiceを取得

switch (choice) {
case '1':
// 預金ロジック
break;
case '2':
// 引き出しロジック
break;
case '3':
// 残高参照ロジック
break;
case '4':
// プログラム終了

// ユーザーに取引終了かどうか問い合わせ
nextTransaction = isContinued();


break;
default:
// 上記に当てはまらない場合
}
}

└──────────────────────────────────┘


■isContinued()メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

では、isContinued()メソッドはどのように設計すればいいでしょうか?
このメソッドの役割は、コンソール上からユーザーがタイプした文字(文字列)を判別し、ユーザーが取引続行を望むならtrueを返し、望まないならfalseを返すようにします。また、ユーザーが指定された文字(文字列)以外をタイプしたときはもう一度ユーザーに入力を促さなければなりませんので、何かループのようなモノも必要でしょう。

┌──────────────────────────────────┐

boolean isContinued() {
while (true) {
//ユーザーから文字(列)を取得

if (文字(列)が取引終了){
return false;
}
else if (文字(列)が取引続行) {
return true;
}
else {
//もう一度ループ
}
}
}

└──────────────────────────────────┘


前回は、プログラム終了の条件にchar型をつかいましたね。char型を条件式に使うと、

┌──────────────────────────────────┐

char choice //ユーザーの選択した値
//プログラムを終了する場合は、'y'なので、戻り値はfalse
if (choice == 'y' || choice == 'Y') {
return false;
}
//プログラムを終了せずに取引を続行する場合は、'n'なので、戻り値はtrue
else (choice == 'n' || choice == 'N') {
return true;
}
else {
System.out.println("入力が正しくありません。"):
}

└──────────────────────────────────┘

というようになりますね。今回は、ユーザーからString型である文字列でその条件をチェックします。String型を比較するときはどのメソッドを使えばいいのでしょう?SDK API仕様をみてください。Stringクラスにはどのようなメソッドがありますか?比較するのだから、compareTo()メソッド?それでもいいですね。ompareTo()メソッドは戻り値がint型で一致した場合は、0を返すので

┌──────────────────────────────────┐

String choice //ユーザーの選択した文字列
if (chocie.compareTo("y") == 0 || choice.compareTo("Y") == 0) {
return false;
}
...
└──────────────────────────────────┘


となりますね。ここで、Cプログラマは注意です。compareTo()で文字列が一致したときに0を返すからといって

┌──────────────────────────────────┐

if (!choice.compareTo("y")) {
...
}

└──────────────────────────────────┘


とは出来ないことに注意して下さい。Javaは型を厳格に区別するのでif文でint型は使えません。

文字列が同じか同じでないかを比較する場合は、compareTo()メソッドより使いやすいメソッドがあります。equals()メソッドです。このメソッドは戻り値がboolean型で比較文字列が同じ場合はtrue、それ以外はfalseを返します。それを利用すると、

┌──────────────────────────────────┐

String choice //ユーザーの選択した値
//プログラムを終了する場合は、'y'なので、戻り値はfalse
if (choice.equals("y") || choice.equals("Y") {
return false;
}
//プログラムを終了せずに取引を続行する場合は、戻り値はtrue;
else (choice.equals("n") || choice.equals("N") {
return true;
}
else {
System.out.println("入力が正しくありません。"):
}

└──────────────────────────────────┘

とできますね。また、String型なので、

┌──────────────────────────────────┐

if (choice.equals("yes") || choice.equals("YES") {
...
}

└──────────────────────────────────┘

というように、一字ではない文字列の比較も出来ます。char型ではこうはできませんね。

ところで、

┌──────────────────────────────────┐

// == を使った例
if ((choice == "y") || (choice == "Y")) {
...
}

└──────────────────────────────────┘

とは出来るでしょうか?答えはNOです。文法的には正しいです。しかし、==(イーコール・イーコール)では思わぬプログラムエラーを招いてしまいます。この場合は、.equals(ドット・イーコール)を必ず使って下さい。

完成したisContinued()メソッドは以下のようになります。

┌──────────────────────────────────┐

/**
* コンソールからの文字列がy、Y、n、Nかをチェックする。
*
* @return true ユーザーがnかNを入力した時
* false ユーザーがyかYを入力した時
* @exception IOException コンソールからの入力時例外が発生した時
*/
boolean isContinued() throws IOException {
BufferedReader buffer = new BufferedReader(
new InputStreamReader(System.in));
while (true) {
String answer = buffer.readLine();
if (answer.equals("y") || answer.equals("N")) {
return false;
}
else if (answer.equals("n") || answer.equals("N")){
return true;
}
else {
System.out.print("入力が正しくありません。" +
"YかNを入力して下さい:");
}
}
}

└──────────────────────────────────┘


ところで、なぜString型の比較には、==ではなくて.equals()なのでしょうか?イーコール・イーコールとドット・イーコールの違いは何なんでしょう?

このカラクリを次号で見ていきたいと思います。次号もをお楽しみに。

Mr.Hack


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

© 2002 MR.HACK ALL RIGHTS RESERVED