Back To Main

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ ◆Javaで珈琲ブレイク vol.017 ◆
┃……………………………………………………………………………………………
┃ [不定期] まぐまぐ ID=0000088576 Melma! ID=m00061296
┃……………………………………………………………………………………………
┃ 今回からご覧になる方は、バックナンバーご活用下さい
┃ http://javacafebreak.tripod.com
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆目次◆

■Question 14-1 checkPassword()、hasUpperLetter()
■Question 14-2
■setAccountId(String accountId)メソッド
■setPassword(String password)メソッド
■getAccountId()メソッド
■equalsToPassword(String userPassword)メソッド
■Question 14-3-1
■Question 14-3-2

みなさん、こんにちは。

Mr.Hackです。

アンケートにご協力頂いた方、本当にどうもありがとうございました。結果は、

http://clickanketo.com/cgi-bin/a.cgi?q00011580a30

から、ご覧いただけます。数名の方からは、感想メールをいただきました。これからもがんばっていかないといけないなと思う限りです。その反面、アンケートにご協力頂けた方が思った以上に少ないのは、少々がっかりしました。回答していただけた方は、一人あたり、家用、オフィス用のメールアドレスを登録しているとして、42 / 1000 * 2 = 約10 % ぐらいなのでしょうか?一人、一つのアドレスと仮定するとその半分、5%ぐらいの回答です。皆さんはすべて常時接続の環境ではないと思うので、アンケートごときにインターネット接続はめんどくさいと思ってらっしゃることはたしかです。しかしながら、インタラクティブにメルマガを発信したいと思っていた僕にとっては、ちょっと残念な結果です。

アンケートの内容は、だいたい予想していた内容でした。中級者の方が1−2度、初心者の方が、2−4読んで理解できる内容でありたいと思っていましたので、回答の中でたまにわからないことがある方が一番多いのは、予想通りです。回答いただけた人のうち、ほぼ理解している人と、だいたい理解している人が64%しめています。完璧に理解している人にとっては、常に刺激的でいれるようにとおもっています。また、ほとんど理解していない方は、24%でした。初心者から、中級者まで皆さんがすべて満足だというメルマガを発行するのは難しいですが、ほとんどわからないと回答された方に対しては、掲示板でのヘルプや発行量、表現の仕方の容易化を目指していこうと思います。

その他、アンケートに回答いただけなかった方がどう思っていらっしゃるかが気がかりですが、アンケートに回答いただけた方を、Javaで珈琲ブレイクに興味を持っている方と、仮に置き換えますと、ほとんどの方が、興味を持っていただけていないということにもなります。これは、極端な見方ですが、シビアに受け止めています。

アンケートの回答率をみて、すこしやる気が失せてしまったのも事実です。メルマガという発信手段から、負担の軽い、ホームページで自分のペースで発信していくのも一つの方法かなと思ったりしています。結局、無料・自動発信されるメルマガということが、皆様のやる気をそいでいるところも、多々ながらあるように感じられます。ホームページで発信しても、本当に勉強をしたい方には、手段が変わっただけで問題はないでしょう。もうすこし、自分のペースでメルマガを発行してみまして、どうしようか決めようと思います。

それでは、今週のトピックに入っていきましょう。先週は、Pseudo Code(擬似コード)を勉強しました。擬似コードとはロジックを考える上でJavaの様な具体的な言語に頼らないコードでした。それを使って、checkPassword()メソッドを考えました。前回の宿題はそのスードコードをJavaに書き換えるというものでした。まず、それから、みてみましょう。すでに、ロジックはみているので、とりわけ問題はないと思います。

■Question 14-1 checkPassword()、hasUpperLetter()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1.上記のPsuedo Code(疑似コード)をJavaで書き直しなさい。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

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

public static boolean checkPassword(String password) {
/**
* passwordがnullか、5文字未満の場合
*/
if (password == null || password.length() < 5) {
return false;
}
/**
* 小文字ではなく、大文字でもなく、かつ、数字でもない場合
*/
char character;
for (int i = 0; i < password.length(); i++) {
character = password.charAt(i);
if ((character < 'a' || character > 'z') &&
(character < 'A' || character > 'Z') &&
(character < '0' || character > '9')) {
return false;
}
}
/**
* 大文字を含まないか、小文字を含まないか、
* 数字を含まないかのいずれかの場合
*/
if (!hasUpperLetter(password) || !hasLowerLetter(password) ||
!hasNumber(password)) {
return false;
}
/**
* 上記の条件を全てクリアーした場合
*/
return true;
}

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

それぞれの、hasUpperLetter()、hasLowerLetter()、hasNumber()メソッドは、それぞれのキャラクタをチェックするところ以外は、全く同じです。

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

private static boolean hasUpperLetter(String stringValue) {
if (stringValue == null || stringValue.length() == 0) {
return false;
}
char character;
for (int i = 0; i < stringValue.length(); i++) {
character = stringValue.charAt(i);

// hasLowerLetter()、hasNumber()とは以下のキャラクタの条件だけ違う
if (character >= 'A' && character <= 'Z') {
return true;
}
}
return false;
}

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

■Question 14-2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

2.下記のメソッドをBankAccountクラスに付け加えなさい。

void setAccountId(String accountId)
String getAccountId()
void setPassword(String password)
boolean equalsToPassword(String userPassword);

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

setAccountId()メソッドは、アカウントIDを後に変更するときに使います。キー(プライマリキー)となるフィールドは変更できないとする場合もありますが、今回は変更を許容しようと思いますので、setAccountId()でaccountId()を変更します。また、後でみるように、コンストラクタでのaccountIdの設定がsetAccountId()メソッドでソースコードがシンプルになります。getAccountId()メソッドは、accountId()の比較や、あるデータ構造に保持するときにaccountIdを引き出せますね。setPassword()メソッドはパスワードの変更に使います。これもsetAccountId()メソッドと同様に、コンストラクタで使いますと、ソースコードがすっきりします。equalsToPassword()メソッドは、ユーザーがタイプした文字列userPasswordを引数にして、オブジェクト内に保持されている(以前設定したパスワード)passwordと比較し、同じなら、trueを返します。

setAccountId()メソッドから具体的にみていきましょう。

■setAccountId(String accountId)メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

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

public void setAccountId(String accountId) {
if (!BankAccount.checkAccountId(accountId)) {
throw new IllegalArgumentException(
"アカウントIDが正しくありません。");
}
this.accountId = accountId;
}

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

checkAccountId()メソッドを使っていますね。accountIdを引数としてcheckAccountId()メソッドに渡してあげますと、そのメソッドは、文字列accountIdが3文字以上の文字列で、大文字か小文字からなる文字列かどうかをチェックします。正しくない場合、つまり、checkAccountId()メソッドがfalseを返す場合、if節の条件はNOT (false) = trueとなりますので、IllegalArgumentExceptionをスローします。これは、以前勉強した、RuntimeExceptionを継承したExceptionをロジックの分岐に使う方法と全く同じですね。ところで、

┌──────────────────────────────────┐
BankAccount.checkAccountId(accountId);
└──────────────────────────────────┘

これって、今までとは、ちょっと違った形をしていますね。どこが違うかわかりますか?BankAccountって何でしたっけ?そうです。クラスでしたね。あれれ?クラスに直接"."(ドット)を用いてメソッドを適用してよかったのでしたっけ?実はいいのです。普通ですと

┌──────────────────────────────────┐
BankAccount account = new BankAccount("MrHack", "mRha9", 0);
account.checkAccountId(accountId);
└──────────────────────────────────┘

となるところですね。まず、accountというBankAccountの参照変数(オブジェクト)を作ってそれにcheckAccountId()メソッドを適用しました。

それに対して、BankAccount.checkAccountId(accountId)は、参照変数(オブジェクト)にcheckAccountId()メソッドを適用する代わりに、BankAccountクラスに直接checkAccountId()メソッドを適用しています。このメソッドを

┌──────────────────────────────────┐
クラスメソッド、または、スタティック(static)メソッド
└──────────────────────────────────┘

といいます。vol.015のcheckAccountId()メソッドをみてください(注:vol.015のcheckAccountId()メソッドはstaticが抜けていました。下記が正しい定義です)

┌──────────────────────────────────┐
public static boolean checkAccountId(String accountId) {
...
└──────────────────────────────────┘

staticというキーワードがついていますね。クラスメソッドは、staticというキーワードがつくのでスタティック(static)メソッドとも言われます。staticがつくとメソッドはクラスメソッドになり、クラス(BankAccount)に直接メソッドを適用できます。。staticキーワードについては、次の次の号で詳しく説明します。ここでは、今までのオブジェクトにメソッドを適用するとの同じように、メソッドの役割をする、と覚えておいてください。つまり、引数として文字列accountIdを渡されたら、checkAccountId()メソッドはそのaccountIdが適切な文字列かどうかをチェックして、適切ならtrueを、不適切なら、falseを返します。

■setPassword(String password)メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

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

public void setPassword(String password) {
if (!BankAccount.checkPassword(password)) {
throw new IllegalArgumentException(
"パスワードが正しくありません。");
}
this.password = password;
}

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

setPassword()メソッド内の、checkPassword()メソッドも同じです。先ほどのcheckPassword()メソッドの定義をもう一度みますと、

┌──────────────────────────────────┐
public static boolean checkPassword(String password) {
...
└──────────────────────────────────┘

とstaticがありますね。checkPasswordもクラス(スタティック)メソッドです。checkAccountId()メソッドと同じように、文字列passwordが不適切な場合はfalseを返しますのでNOT (false) = trueで、IllegalArgumentExceptionをスローします。

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

これは、BankAccountクラス内のインスタンスフィールドであるaccountIdを戻り値として返してあげればいいので、

┌──────────────────────────────────┐
public String getAccountId() {
return this.accountId;
}
└──────────────────────────────────┘

でいいですね。問題ないと思います。


■equalsToPassword(String userPassword)メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

こちらも、文字列(String型)同士の比較ですので、String#equals()メソッドをつかって比較すればいいと思います。そのとき、equals()メソッドは文字列の内容が一致すればtrueを返しますので、そのtrueをそのままequalsToPassword()メソッドの戻り値にしてあげればいいとお思います。

┌──────────────────────────────────┐
public boolean equalsToPassword(String userPassword) {
return this.password.equals(userPassword);
}
└──────────────────────────────────┘

そうすると、上記のように一行で書けてすっきりしますね。

■Question 14-3-1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

3−1.BankAccountクラスに、以下のコンストラクタを付け加えなさい。

BankAccount(String accountId, String password, double amount)
BankAccount(String accountId, String password)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

以前のBankAccountクラスのコンストラクタは、引数がなく、残高をゼロにセットするBankAccount()コンストラクタと、任意の残高を引数としてセットするBankAccount(double amount)コンストラクタがありました。今回は、フィールド(instance field)として、String型のaccountIdとString型のpasswordが加わり、BankAccountオブジェクト生成(口座開設)するときに、アカウントIDとパスワードを忘れずに設定したいですね。アカウントIDは各口座開設者一人一人のIDを表しますから、コンストラクタでの初期化は必須でしょう。パスワードにおいても口座開設後すぐにパスワードを使ってアクセス可能なように、コンストラクタでの初期化時に設定するのが便利だと思います。

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

public BankAccount(String accountId, String password, double amount) {

/// 新たに加わった部分 開始

//不適切な文字列の時IllegalArgumentExceptoinをスロー
setAccountId(accountId);
//不適切な文字列の時IllegalArgumentExceptoinをスロー
setPassword(password);

///新たに加わった部分 終わり

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

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

すでに、accountIdをセットするsetAccountId()メソッドとpasswordをセットするsetPassword()メソッドを作っていますので、それを利用しましょう。そうすると、以前のコンストラクタに2行付け加えるだけで、完了です。ここで注意しないといけないのは、setAccountId()、setPassword()それぞれRuntime ExceptionであるIllegalArgumentExceptionをスローすることを注意する必要があります。setAccountId()、setPassword()メソッドを使うクライアント(使う側)でtry/catch文を用意していない場合は、そのまま次のBankAccountコンストラクタを使うクライアント側でキャッチしてあげないといけませんね。つまり、投げられた例外(Exception)はBankAccount()コンストラクタ内を通り越して、引き続きスローされ続けます。それをこんどは、BankAccount()コンストラクタを使うクライアント(例えば、BankUserInterfaceクラス)でtry/catch文を用いてキャッチしてあげなければなりません。このRuntimeExceptionを継承しているIllegalArgumentExceptionはメソッドて定義でthrowsキーワードを使わなくてもいいですし、コンパイル時にもチェックされませんので、javadocで明示的に@exceptionタグを使って表記しなければなりませんでしたね。表記しないとBankAccount()コンストラクタを使うユーザー(プログラマ)に不意の予測を強いることになるからでしたね。Readability、Reusabilityという観点からすれば、setAccountId()、setPassword()メソッドに簡単なIllegalArgumentExceptionがスローされることを記してもいいですね。または、

┌──────────────────────────────────┐
try {
setAccountId(accountId);
setPassword(password);
}
catch (IllegalArgumentException e) {
throw e;
}
└──────────────────────────────────┘

と明示的にしてもいいですね。ただ、こうする場合は、

┌──────────────────────────────────┐
try {
setAccountId(accountId);
setPassword(password);
}
catch (IllegalArgumentException e) {
}
└──────────────────────────────────┘

catch節で何もしないのはダメですよ。なぜなら、setAccountId()、setPassword()メソッドで実行時にスローされた例外がそのcatch節でキャッチされたまま、その例外やメッセージはこれ以上は伝わらない(スローされない)からです。引き続き伝えるためには、

┌──────────────────────────────────┐
throw e;
└──────────────────────────────────┘

と引き続きスローし続けなければなりません。ここはとても重要なところであり、注意するところです。

もう一つのコンストラクタBankAccount(String accountId, String password)は、先ほどのBankAccount(String accountId, String password, double amount)をthisキーワードを用いて書けば、一行でいいですね。ここでのthisは『この』クラスのコンストラクタという意味でした。

┌──────────────────────────────────┐
public BankAccount(String accountId, String password) {
this(accountId, password, 0);
}
└──────────────────────────────────┘


■Question 14-3-2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

3−2.それに伴い、BankAccount()、BankAccount(double amount)の
コンストラクタを『推奨されない』コンストラクタとしなさい
(Javadocの@deprecatedタグ)。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

最後に、今まで使ってきたコンストラクタ、BankAccount()、BankAccount(double amount)は不要になりました。むしろ、インスタンス生成時はaccountIdを必ず初期化したいので、消してしまいたいぐらいですね。しかしながら、以前のコンストラクタ二つを用いてインスタンスを生成しているクライアント(使う側のクラス)があるかもしれません。今回のバージョンアップでいきなりそのコンストラクタが使えなくなってしまっては、BankAccountクラスを使うシステムに多大な影響が出る可能性があります。それでは困りますね。そのために、緩やかに、古いコンストラクタを使わないように、ユーザー(プログラマ)に働きかけなければなりません。それを担うのが、Javadocの@deprecatedタグでした。このタグだけは例外で、コンパイル時にクラスの中にメモ書きを残します。そしてタグの付いたメソッド・コンストラクタを使うクラスがあると『推奨されない』というメッセージを出します。これによって、ユーザーには新しいコンストラクタを使うように、促すことができます。

長くなりましたので、次回にソースコードをみてみましょう。

それでは、みなさん、またあいましょう。

Mr.Hack


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

© 2002 MR.HACK ALL RIGHTS RESERVED