広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2009年12月30日

    Java総合講座 - 初心者から達人へのパスポート
                  2009年11月開講コース 007号

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:毎週日曜の夜に発行
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:毎週水曜の夜に発行
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(2009年11月開講コース)007号
 (当記事はvol.007のリバイバル(revival)版です。)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


========================================================
◆ 01.setterメソッドとgetterメソッド
========================================================

前回までにお話したHumanクラスでは、sayName( )メソッドは氏名を
ディスプレイに表示していました。でも、読者の皆さんは、これはちょっと
へんだと気づいたのではないでしょうか。

現実の世界では、人に何かを聞かれたら、聞いた人に返事を返すはずです。
氏名を聞かれたら、聞いた人に答を返すはずです。

それと同じように、プログラム上のオブジェクトでも、氏名を聞かれたら
聞いたオブジェクトに答を返すべきであって、いきなりディスプレイに答を
表示するのはおかしいでしょう。

実は、前回のプログラムのsayName( )メソッドは、オブジェクトに演劇風
の振る舞いをさせるために、いきなりディスプレイに会話文を表示するよう
にしたもので、氏名を答える普通のメソッドとは異なります。

実際、氏名を答えるメソッドは、氏名を聞いたオブジェクトに答を返すのが
普通です。
そして、答を返すというのは、メソッドの戻り値を返すということなのです。

それだけでなく、現在のJavaでは、属性の値を設定(記憶)したり取り出し
(読み出し)したりするメソッドの名前には標準があり、属性の名前の前に
setやgetをつける習慣になっています。つまり属性の名前がXXXであれば、
setXXX()やgetXXX()というメソッド名にします。

そして、これらをそれぞれセッター・メソッド(setter method)、ゲッター・
メソッド(getter method)と呼びます。
なお、当メールマガジンでは、簡単のためにこれらをsetメソッド、getメソッド
と呼ぶことがあります。

つまり、属性に値を設定するメソッドがsetメソッドで、属性から値を取り出
すメソッドがgetメソッドです。

┌補足─────────────────────────┐
英語では、setterやgetterのように動詞の後ろにerがつくと、
その行為を行うもの(者または物)を意味します。したがって、
setterは「set(設定)という行為をするもの」、getterは「get
(取得)という行為をするもの」というような意味になります。
ところが日本ではこのような習慣はないため、「セッター」や
「ゲッター」という言葉を使っても正しく意味を理解することは
できず、たんなる専門用語としてしか認識されない恐れがありま
す。
そこで、当メールマガジンでは、日本人にとってはセッターや
ゲッターという言葉を使うよりもsetメソッド、getメソッドと
いう呼び方をしたほうがかえってわかりやすいと考え、このよう
な呼び方を採用しています。
市販の普通のJava関連書では、setメソッド、getメソッドという
言葉を使っている本は少ないと思いますので、その点ご注意くだ
さい。
└───────────────────────────┘

詳しいことは、のちにJavaBeansのお話をするときに述べますが、
今回のname(氏名)という属性の場合は、それぞれ、setNameやgetName
という名前になります。
つまり、氏名を答えるメソッドはgetNameという名前にするのが標準的なの
です。

getName( )メソッドの定義はたとえば下記のようになります。

public String getName() {
   return name;
}

このメソッドは、戻り値がString型です。
これは、このメソッドを呼び出したオブジェクトにString型の値を返すこと
を意味します。

そして、

return name;

という文が、nameの値を戻り値として返すことを意味しています。
returnには返すという意味がありますね。

nameはString型でしたね。returnで返す値は、当然ながら戻り値の型と
合っていなければなりません。
もし合っていないときは、コンパイラーがエラーを返します。

┌補足─────────────────────────┐
name変数に記憶されているものは、実はStringオブジェクトの
アドレスですから、nameの値を戻り値として返すというのは実際
にはnameに記憶されているアドレスを返すことを意味します。
└───────────────────────────┘

setName( )メソッドの定義はたとえば下記のようになります。

public void setName(String aName) {
   name = aName;
}

getName( )メソッドが作られたら、sayName( )を次のように書き換え
ることができます。

public void sayName( ) {
      System.out.println("My name is " + getName() + ".");
}

これは、nameという変数を使っていたところをgetName()メソッドの呼び
出しに変えただけです。

元のnameの変数のままにしていても同じじゃないかと思われるかもしれ
ません。
確かにsayName( )メソッドの働きは同じなのですが、name変数を直接
使うのではなくgetName( )の呼び出しにすると、それなりのメリットが
生まれます。

┌補足─────────────────────────┐
たとえば、nameという属性は、将来firstNameとfamilyNameという
2つの属性に分割することになるかもしれません。
その場合、sayName( )メソッドの中でnameを使っていると、
getName( )メソッドの中とsayName( )メソッドの中の両方ともに
変更を加えなければならなくなります。

それに対して、sayName( )メソッドの中ではnameではなくgetName( )
メソッドを呼び出しているとすると、sayName( )メソッドを変更
する必要はなく、getName( )メソッドだけを変更すればいいこと
になります。

nameを直接使っていたところをgetName( )の呼び出しに変えると
パフォーマンス(性能)が悪くなるのではないかと心配する人も
いますが、ほとんど問題になることはありません。
└───────────────────────────┘

なお、通常のメソッド呼び出しは、メソッド名の前にオブジェクトの
指名(通常は変数名)がつきますが、上記のsayName( )の中のgetName( )
のように、自分のメソッドを呼び出すときは、オブジェクトの指名は不要
です。

どうしてもオブジェクト(今の場合は自分自身)を指名したい場合は、

this.getName()

というようにthisというキーワードを指定します。

thisは文字通り自分自身のオブジェクトを指します。


では、実際にHumanクラスにsetName( )メソッドとgetName( )メソッド
を組み込んでみましょう。

Eclipseには、属性の変数に対してsetメソッド、getメソッドを自動的に
作成してくれる機能がありますので、この機能を使うことにします。

(1)Eclipseを起動しましょう。

(2)Human.javaファイルを開きましょう。
「パッケージ・エクスプローラー」タブの下にあるJStudy1(前回作成した
プロジェクト名)の配下のsrcの配下のjp.co.flsi.lecture.humanの配下の
Human.javaをダブル・クリックしてください。

(あるいは前回Human.javaファイルを開いたままにしていた場合は、
「Human.java」タブがあるはずなので、そのタブを1回クリックする
だけでHuman.javaのファイルが前面に表示されます。)

(3)Human.javaのソース・コードの中の

private String name;

の行のnameをダブル・クリックし、色が反転した状態にしてください。
(もしくはnameを1回クリックして、その中にカーソルがはいった状態にします。)

(4) メニュー・バーから「リファクタリング」→「フィールドのカプセル化」
を選択します。

(5)「フィールドのカプセル化」ウインドウが開きます。

「新規メソッドを次の後ろに挿入」という項目は、getメソッドやsetメソッド
を挿入する位置を指定するものですが、(どの位置でもかまわないのですが)
わかりやすくするために「最初のメソッドとして」を選択しておきましょう。

(6)「アクセス修飾子」はpublicを選択してください。
これでgetメソッドやsetメソッドのアクセス制御がpublicになります。

(7)「宣言型でのフィールド・アクセス」は、「setterとgetterの使用」
が選択されたままにしましょう。
これで、たとえば先ほどのsayName( )メソッドの中のnameがgetName( )に書き
換えられるなどのように、変数へのアクセスがセッター・メソッドやゲッター・
メソッドの呼び出しに変更されます。

(8)「プレビュー」ボタンをクリックして、どのような変更がなされるのかを
確認したあと、「OK」ボタンをクリックします。

┌補足─────────────────────────┐
以前にもお話したように、属性を表現する変数のことをJavaでは
「フィールド」(field)と呼ぶことがあります。
「フィールドのカプセル化」とは属性の変数にsetメソッド、get
メソッドを用意し、変数に直接アクセスさせないようにすること
を意味します。
└───────────────────────────┘

これで、setName( )メソッドとgetName( )メソッドが自動的に組み込まれ、
sayName( )メソッドの中のnameがgetName( )に書き換えられました。

Human.javaのソース・コードを確認してください。


ところで、先日作成したmemorizeName( )メソッドもsetName( )メソッドと
同じことをしているのでしたね。

memorizeName( )の定義を確認してください。

public void memorizeName(String aName) {
   setName(aName);
}

というように、このメソッドの中のname変数にアクセスしていたところが
setName( )メソッドの呼び出しに書き換えられていますが、これはmemorizeName( )が
やるべきことをsetName( )に委託しているだけですね。

自分は仕事をさぼって、他人にやらせているようなものです。

このmemorizeName( )メソッドを呼び出しているところをsetName( )メソッドの
呼び出しに書き換えると、もはやmemorizeName( )メソッドは必要なくなります。

というわけで、memorizeName( )メソッドの呼び出しをsetName( )メソッドの呼び
出しに書き換えましょう。以下のように操作します。

(9)Human.javaのソース・コードの中でmemorizeNameの文字列をダブル・クリック
します。(もしくは1回クリックしてカーソルを入れる。)

(10) メニュー・バーから「リファクタリング」→「名前変更」を選択します。

(11) 名前をmemorizeNameからsetNameに書き換えてください。
そして、Enterキーを押します。


(12)次の画面では問題の報告がされますが、これはHuman.javaの中には
memorizeName( )メソッドの定義とsetName( )メソッドの定義の両方があり、
memorizeNameの名前をsetNameに変えようとすると、既に存在するsetNameメソッド
と重複してしまうからです。
でも、無視して、「継続」ボタンをクリックしてください。

(13) これでHumanTheater.javaの中のmemorizeName( )メソッドの呼び出しが
setName( )メソッドの呼び出しに書き換えられました。

HumanTheater.javaを開いて確認してください。
(先ほどHuman.javaを開いたのと同様の操作をします。)

これで、memorizeName( )メソッドを呼び出している個所はなくなりましたので、
memorizeName( )メソッドの定義は不要です。しかし、すでにmemorizeName( )メ
ソッド自身がsetName( )メソッドに改名されているので、現在setName( )メソッド
が二つあることになり、重複しているのでエラーになっている(赤い下線が付き、
赤い×マークが付く)はずです。

(「Human.java」タブをクリックしてHuman.javaのソース・コードを前面に表示し)
Human.javaの中の

public void setName(String aName) { // [4行目]
      setName(aName);     // [5行目]
   }   // [6行目]

のほうがmemorizeName( )メソッドから改名した部分なので、これら3行を削除して
ください。(ドラッグして選択状態にし、Deleteキーを押して削除する。)

(なお、インデント(字下げ)がくずれてしまったときは、Ctrl+Aキー(Ctrlキーを押し
ながらAキーを押すという意味)で全行を選択状態にし、Ctrl+Iキーを押すと自動的に
字下げが行われます。)


これで、エラーは無くなりましたね。では、Human.javaを保管し、HumanTheater.java
を実行して結果が以前と変わらないことを確認しましょう。

┌補足─────────────────────────┐
ちなみに、HumanTheater.javaの中のmemorizeName( )メソッド
の呼び出しをsetName( )メソッドの呼び出しに変更せずに、先に
Human.javaの中のmemorizeName( )メソッドの定義を削除してし
まった場合、Eclipseが自動的にHumanTheater.java側でエラーを
報告してくれるので、わかります。
└───────────────────────────┘

こういう作業を「リファクタリング」(refactoring)と言い、今の段階では重要性は
感じられないかもしれませんが、一般のプログラム開発ではとても重要な作業になります。

リファクタリングというのは、プログラムの機能を維持したままプログラムを改良して、
プログラムを読みやすく、理解しやすく、変更しやすくすることを意味するのですが、
本格的なシステム開発の話をするときに別途詳しく説明いたします。



========================================================
◆ 02.コンストラクターの作成
========================================================

ここまでのHumanクラスでは、あまりにも芸がなさすぎるので、今回はもう少し複雑な
メソッドを追加してみましょう。

今回は、Humanクラスに年齢を計算して答えるメソッドを作りこむことにします。

そもそも、生年月日は人の属性の一つですから、変数に記憶させることにします。

年と月と日をバラバラの変数にするのでは面倒くさいですね。でもその必要はありま
せん。

Javaには、西洋のカレンダー(グレゴリー暦)のクラスが用意されており、年月日は
このカレンダーのオブジェクトに記憶させておく(カレンダー上でその年月日を指し
示しておく)ことができます。

そうすれば、生年月日を表す変数はこのカレンダー型の変数一つですみます。

グレゴリー暦のクラス名は、その名もずばりGregorianCalendarといいます。

たとえば、1970年8月1日生まれの人を考えてみましょう。

カレンダーのオブジェクトを生成して1970年8月1日の日付を指し示しておきたいときは

GregorianCalendar  dateOfBirth = new GregorianCalendar(1970, 7, 1);
                                /* 8月は7を指定することに注意。 */

というようにします。

ここでは、年、月、日の3つの引数を持つコンストラクターを使っています。

ちょっと注意して欲しいことは、1月は1ではなく0で指定し、8月は7というように
日本の月の数より1少ない数を指定することです。
また、年は西暦の4桁を使いましょう。平成18年だから18を入力しようなんて考えては
いけません。このクラスは西暦のクラスなのです。

上記の文では、1970年8月1日を指し示したカレンダーのオブジェクトが生成され、
それがdateOfBirthという変数に代入されます。

ところで、人の氏名は必ずしも生まれた時から決まっているわけではありませんし、
結婚して名字が変わるとか、養子になって変わるとか、何らかの理由で人生の途中で
変わることがありますから、setName( )メソッドで変更できるようにしておく必要が
あるでしょう。

しかし、生年月日は人生の途中で変わることはなく、人の誕生の時点で決まってしまい
ます。
このような場合には、setメソッドを用意する必要はなく、逆に用意しないことによって、
生年月日を変更できないようにします。

では、setメソッドを用意せずしてどうやって生年月日を設定するのかといえば、
Humanのコンストラクターで設定すればいいのです。

次のように、Humanのコンストラクターを定義してみましょう。

public Human(int birthYear, int birthMonth, int birthDate) {
   dateOfBirth = new GregorianCalendar(birthYear, birthMonth - 1, birthDate);
}

先ほどお話したように、GregorianCalendarの月の数は日本の月の数より1少ないですから
上のように1を引いておきます。

なお、コンストラクターは特殊なメソッドで、戻り値は持ちませんがvoidの指定もしませ
んので、ご注意ください。

これに合わせて、生年月日の属性は次のように宣言しておくことができます。

private final GregorianCalendar  dateOfBirth;

ここで、finalというのは「最終的な」という意味のキーワードですが、これが変数の
宣言時に指定されていると「この変数に一度、値を代入すると、それは最終的なものに
なり、以後変更することはできない」ことを意味します。

こうしておけば、さきほどのHumanのコンストラクターで生年月日を設定すると、それ
以後は変更できないことになります。

そもそもsetメソッドがなければdateOfBirthの変更はできないので、finalの指定を
しなくてもだいじょうぶなのですが、念のために指定しておきます。

私なんかは忘れっぽいので、そのうちにdateOfBirthの意味を忘れてしまって、あとで
まちがえてsetメソッドを作ってしまうというようなことがありえるのですが、そのよう
な場合でも、finalが指定してあれば、エラーになるので気がつくのです。

現在の日付を指し示すGregorianCalendarオブジェクトはもっと簡単に生成できます。

GregorianCalendar  today = new GregorianCalendar();

上記のように引数なしのコンストラクターを呼び出すと、現在の日付を指し示すカレン
ダーのオブジェクトが生成されるのです。

上記の式では、それがtodayという変数に代入されます。

(もっとも、コンピューターの内蔵時計がまちがった日付を指していると、このオブジェ
クトもまちがった日付を指してしまいます。)


それでは、年齢を計算して答える(戻り値で返す)メソッドを書いてみましょう。

その前に、年齢計算をするにあたっては、本日が誕生日の前か後かを判断し、その条件に
応じて計算式を変える必要がありますから、条件を指定する方法についてお話しておきま
しょう。



========================================================
◆ 03.if文
========================================================

Javaでは、条件に応じて式を取り替えられるようにするために、if文というものが用意さ
れています。

その構文は下記のようになります。

    if (条件) 条件が真のときに実行する文;

条件が真のときに実行したい文が複数あるときには、下記のようにそれらを{ }で囲みます。

    if (条件) {条件が真のときに実行する文1; 条件が真のときに実行する文2; ...}

ちなみに、{ }で囲った部分をブロック(block)と言い、これは複数の文を組み合わせた、
いわゆる複文を表現します。
つまり{ }で囲むことによって、複数の文を一つのまとまった文のように扱うことができ
ます。

条件が真のときに実行したい文が一つだけの場合は、{ }で囲んでも囲まなくても同じです。

if文は、「条件」の真偽を判定し、条件が真だった時にのみ、「条件が真のときに実行する文」
あるいは{}の中を実行します。
ifは日本語では、「もし〜ならば・・・」という意味ですから、容易に理解できると思います。

if文にはまた、

    if (条件) {条件が真のときに実行する文;...} else {条件が偽のときに実行する文;...}

という構文形式もあります。

この場合は、ifの条件が偽だった時には、elseの右の{ }の中を実行することになります。
これをif-else文と呼ぶことがあります。
elseが「さもなければ...」という意味であることから、容易に理解できると思います。

さらには、else ifというようにelseの後ろにif文を組み合わせて使うこともできます。


たとえばaとbというint型の変数があった場合に「aの値とbの値が等しいならば、aに0を代入
する」というようなif文にしたいときは

if (a == b) a = 0;

と書きます。

間違えてはいけないことは、等しいかどうかを判断するためには=ではなく、==を使う
ということです。

=は代入演算子なので、等しいかどうかの判断はしてくれません。

一方、==は右辺と左辺が等しいかどうかを判断する演算子です。

たとえば、aの値とbの値が等しければa == bの演算結果が真となり、a = 0が実行されること
になります。

aの値とbの値が等しくないときはa == bの演算結果が偽となり、a = 0は実行されません。

条件の真偽を判断する演算子には==以外にも以下のようなものがあります。
詳しいことは、のちほどまとめてお話します。

    演算子    意味 
     >     左辺が右辺より大きい
     >=    左辺が右辺より大きいか、または等しい
     <    左辺が右辺より小さい
     <=    左辺が右辺より小さいか、または等しい
     ==    左辺と右辺が等しい
     !=    左辺と右辺が等しくない

なおこれらの演算子は、左辺と右辺がプリミティブ型の場合に使うものと思ってください。

String型などのオブジェクトの場合の取り扱いについては、のちほどまとめてお話します。


それでは、このif文を使って年齢を計算して答える(戻り値で返す)メソッドを書いてみま
しょう。

下記がその例です。(年齢の計算にはもっと簡単なやり方があるのですが、ここではif文
を学んでいただくために、わざわざ面倒なやり方をしています。)

public int getAge() {
  GregorianCalendar today = new GregorianCalendar(); // 1行目
  if (today.get(Calendar.MONTH) > dateOfBirth.get(Calendar.MONTH)) return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR); // 2行目
  if (today.get(Calendar.MONTH)  == dateOfBirth.get(Calendar.MONTH)) { // 3行目
     if (today.get(Calendar.DAY_OF_MONTH) >= dateOfBirth.get(Calendar.DAY_OF_MONTH)) return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR); // 4行目
  } // 5行目
  return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR) - 1; // 6行目
}

(if文が長いため、メールでは1行があふれて複数行になってしまったりして、見づらいかも
しれませんが、Eclipseで入力すると1行に収まります。)

1行目はさきほど説明したのと同じものですね。

2行目を見てみましょう。

get(Calendar.MONTH)はGregorianCalendarの(正確には、そのスーパークラスの)
メソッドで、月の値を取り出してくれます。その戻り値はint型です。

したがって、

today.get(Calendar.MONTH)

は、現在の月を取り出し、

dateOfBirth.get(Calendar.MONTH)

は生年月日の月を取り出します。

そして、

today.get(Calendar.MONTH) > dateOfBirth.get(Calendar.MONTH)

は現在の月が誕生日(生年月日)の月より後かどうかを判断していることになります。

そして、if文によって、この判断の結果が真であれば

return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR);

を実行してくれることになります。

ここで、get(Calendar.YEAR) はGregorianCalendarの(正確には、そのスーパー
クラスの)メソッドで、年の値を取り出してくれます。その戻り値はint型です。

したがって、

today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR)

は、現在の年から生年月日の年を引くことを意味します。

returnというキーワードの後ろにこのように式を書くと、まず式を先に計算してから
その結果をメソッドの戻り値として返すことになります。

戻り値として返すということは、その時点でメソッドの実行も終了してしまうことを
意味します。したがって、あとの文は実行されません。

ところが、もしこのif文の条件文が偽という結果に終わると、このreturn文は実行され
ませんから、メソッドの実行は終了せず、そのまま次の文(3行目〜5行目)に進みます。

3行目の

if (today.get(Calendar.MONTH)  == dateOfBirth.get(Calendar.MONTH))

では、現在の月と誕生日の月が同じかどうかを判断しています。

その結果が真の場合、つまり現在の月と誕生日の月が同じ場合には、{ }の中
すなわち4行目が実行されます。

4行目の条件

today.get(Calendar.DAY_OF_MONTH) >= dateOfBirth.get(Calendar.DAY_OF_MONTH)

では、現在の日が誕生日以後かどうかを判断しています。

ここで、DAY_OF_MONTHというのは、月のうちの何日目かを意味します。

他にDAY_OF_YEARというのもあり、こちらは年のうちの何日目かを意味し、たとえば、
2月20日は、DAY_OF_MONTHで表現すると20ですが、DAY_OF_YEARで表現すると51になり
ます。

注意が必要なことは、たとえば3月1日をDAY_OF_YEARで表現する場合、うるう年では61、
うるう年でないときは60というように、年によって数値が違ってしまうということです。

年月日を扱うときは、うるう年などの変則的な年についても考慮しておく必要があること
を頭に留めておきましょう。

なお、GregorianCalendarは、その年がうるう年かどうかもちゃんと判断してくれます。

┌補足─────────────────────────┐
GregorianCalendar(正確にはそのスーパークラスのCalendar)の
get(Calendar.YEAR)やget(Calendar.MONTH)やget(Calendar.DAY_OF_MONTH)
メソッドは、実は

public int get(int field)

という形式の同一のメソッドです。

int型の引数の値に応じて年の数を返したり月の数を返したり、日の数を
返したりするのです。

そして引数が1のときには年の数を返します。

実はCalendar.YEARは、Calendarのクラス定義の中で

public static final int YEAR = 1;

というように宣言されているものです。

publicですから誰でも使用できますし、finalですから変更不可能つまり
定数として扱われます。

また、staticという指定は、クラスのインスタンスを生成しなくても使える
変数であることを意味し、この指定があると

Calendar.YEAR

というように変数名の前にクラス名をつけて呼び出すことができます。

(staticが指定された変数を「static変数」あるいは「クラス変数」と呼びます。)

したがって、get(Calendar.YEAR)と記述するのはget(1)と記述するのと同じこと
なのですが、見た目にはget(Calendar.YEAR)のほうがget(1)よりも意味がわかり
やすいので、このような指定の仕方をするのです。

Calendar.MONTHやCalendar.DAY_OF_MONTHやCalendar.DAY_OF_YEARに
ついても同様です。
└───────────────────────────┘


この4行目の条件の判断が真、つまり、現在の日が誕生日以後だということになると、

return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR);

が実行されます。return文が実行されると、このメソッドは終了します。

現在の日が誕生日以後でない場合はこのreturn文は実行されず、6行目の文

return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR) - 1;

に進みます。

つまり、現在の月が誕生日の月より前であるか、あるいは、誕生日と同じ月であっても
日が誕生日よりも前であれば、このreturn文が実行されます。

今までのreturn文と違うところは、最後に1を引いていることです。

まだ誕生日が来ていないわけですから、単純に年の引き算をしたときより1歳少なく
するわけです。

以上でgetAge( )メソッドの内容は理解できましたね。



ところで、GregorianCalendarというクラスはjava.utilというパッケージにはいっ
ています。

この場合、本来なら

java.util.GregorianCalendar

というようにパッケージ名をクラス名の前につけて指定する必要があります。

しかし、毎回パッケージ名をつけるのは面倒ですね。そこで、プログラムの先頭に

import java.util.GregorianCalendar;

という文を一度書いておけば、クラス名の前のjava.utilを省略できることになって
います。

┌補足─────────────────────────┐
Eclipseでは、自動的にimport文を挿入してくれる機能があるので、
自分でimport文を書かなくてもすみます。
└───────────────────────────┘

あるいは

import java.util.*;

というように、クラス名の代わりに*を書いたimport文にしておけば、GregorianCalendar
だけでなく、java.utilパッケージにはいっているすべてのクラス名に対してクラス名の前
のjava.utilを省略することができます。

┌補足─────────────────────────┐
これまで使ってきたSystemクラスやStringクラスはjava.langという
パッケージにはいっているのですが、

import java.lang.*;

なんて文は書いた覚えはありませんね。

実は、java.langは一番基本的なパッケージなので、import文を書か
なくても、コンパイラーが自動的にimportしてくれることになって
いるのです。
└───────────────────────────┘


それでは、Humanクラスにこれらのコードを組み込んでみましょう。

Eclipseの「Human.java」タブ(Human.javaという文字列)をダブル・クリックして
ください。
ソース・コードが全面に広がって表示されて長い文も入力しやすくなりますね。

そこでまず、属性のdateOfBirth変数の宣言

private final GregorianCalendar  dateOfBirth;

をname変数の宣言文の下に(新しい行を挿入して)入力してください。

GregorianCalendarというクラス名を入力するときは、Greあたりまで入力したら
Ctrl+スペース・キーでコンテンツ・アシストの機能を使い、リストから選択して
ください。
クラス名が自動的に入力できるだけでなく、class文の上に自動的に

import java.util.GregorianCalendar;

というimport文が組み込まれることがわかりますね。

次に、コンストラクターの定義

public Human(int birthYear, int birthMonth, int birthDate) {
  dateOfBirth = new GregorianCalendar(birthYear, birthMonth - 1, birthDate);
}

をそのdateOfBirth変数の宣言文の下に(新しい行を挿入して)入力してください。

次に、getAge( )メソッドの定義

public int getAge() {
  GregorianCalendar today = new GregorianCalendar(); // 1行目
  if (today.get(Calendar.MONTH) > dateOfBirth.get(Calendar.MONTH)) return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR); // 2行目
  if (today.get(Calendar.MONTH)  == dateOfBirth.get(Calendar.MONTH)) { // 3行目
     if (today.get(Calendar.DAY_OF_MONTH) >= dateOfBirth.get(Calendar.DAY_OF_MONTH)) return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR); // 4行目
  } // 5行目
  return today.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR) - 1; // 6行目
}

をsayName( )メソッドの下にでも書き込んでください。

today.get(Calendar.MONTH)

の部分を入力するときには、today.gまで入力してからコンテンツ・アシストの機能で

get(int field) int - Calendar

というメソッドを選択してください。

┌補足─────────────────────────┐
get(int field)というメソッドは上記の表示からもわかるように
GregorianCalendarのメソッドではなく、そのスーパー・クラスの
Calendarのメソッドです。
GregorianCalendarはそれを継承しているのでGregorianCalendar
でもget(int field)が使えるのです。

なお、上記のget(int field)の後ろに書いてあるintは、このメソッド
の戻り値がint型であることを意味します。
└───────────────────────────┘

そして次に、( )の中の引数Calendar.MONTHを入力するときには、Calあたりまで
入力してからコンテンツ・アシストの機能で

Calendar - java.util

を選択すると、先ほどのimport文と同様に

import java.util.Calendar;

が自動的にclass文の上に挿入されるのがわかります。


このようにコンテンツ・アシスト機能をうまく利用しながら、入力してみてください。


字下げがおかしくて見づらくなった人は、全行を選択(ドラッグすることによって
色を反転させるか、もしくはCtrl+Aキー)してCtrl+Iキーを押すと、自動的にきれい
に字下げしてくれます。

入力が終わったら、保管をしてください。
各行の左側にエラー(赤い×マーク)が表示された場合は、ソース・コードをよく確認
して入力間違いを修正したあと、再度保管しなおしてください。

(「HumanTheater.java」タブのほうに赤い×マークが出るのは、このあとで解決します。)

保管がおわったら、再度「Human.java」タブをダブル・クリックすると、ソース・コード
の表示部分がもとのサイズに戻ります。


今度は、HumanTheater.javaのほうを開いて見てください。

Human hanako = new Human();

の行がエラー(赤い×マークがついている)になっているでしょう。
なぜだか、わかりますか。

今回は、Humanクラスのコンストラクターとして引数つきのメソッドを定義しましたが、
そうするとJavaはデフォルトのコンストラクターを用意してくれません。

したがって、引数なしのコンストラクターがなくなってしまったのです。

Human hanako = new Human();

の左側の赤い×マークにマウス・ポインターをのせてみてください。
「コンストラクターHuman( )は未定義です。」という説明が表示されるでしょう。

はい、では、

Human();

の部分を

Human(1970, 8, 1);

に書き換えて保管しましょう。
これで、エラーはなくなりましたね。


以上で、今回のお話は終わりにします。
Eclipseを終了してください。


ところで、Javaには、これまでに出てきたSystemやStringやGregorianCalendarだけ
でなく非常に多くのクラスがあらかじめ用意されており、これらのクラス群のことを
APIと呼ぶことがあります。

APIというのは、Application Programming Interfaceの略で、もともとはアプリケー
ション・プログラムからOS(基本ソフト)の機能を呼び出すための窓口のような役割を
するプログラム(通常はC言語の関数として提供される)のことを意味していましたが、
JavaのAPIはOSの機能以上にさまざまな機能を持った豊富なJavaのクラスを提供してくれ
ます。


JavaのAPIの詳しい仕様は

http://java.sun.com/javase/ja/6/docs/ja/api/index.html
(Java Platform, Standard Edition 6のAPIリファレンス)

などから調べることができます。
Javaに詳しくない段階では読んでもチンプンカンプンかもしれませんが、Javaに慣れて
きたら、どんなクラスがあるのか時々ながめてみてください。

そのうちに必要になったときに、このAPIリファレンスの使い方についても説明いた
します。



================================================
◆ 04.演習問題
================================================

1. 今回Humanクラスに組み込んだgetAge( )メソッドを呼び出して、
ディスプレイにHanakoさんの年齢を表示させるようにHumanTheaterクラスを
変更してみてください。

2. Humanの氏名(name)の値を一度しか設定できないようにするためには
どうすればいいですか。考えてみてください。



======================================================
◆ 05.前回の演習問題の答
======================================================

     What is your name?
     My name is Suzuki.

というように、HanakoのところがSuzukiに置き換わって表示されます。
理由はわかりますね。



◆編集後記◆━━━━━━━━━━━━━━━━━━━━━━━━

☆最後まで読んでくださってありがとうございます。

☆次回は、ウィンドウを表示するプログラムを作り、ウィンドウを
通してHumanオブジェクトに何かをさせてみます。
お楽しみに。

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



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2009 Future Lifestyle Inc. 不許無断複製