Back To Main

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

◆目次◆

■米国IBM事情
■BankAccount.checkAccountId()とaccount.checkAccountId()
■クラス”の”メンバ ( members of a class)
■インスタンス・フィールド
■クラス・フィールド
■クラス・メソッド
■インスタンス・メソッド
■1.staticなメソッドからは、インスタンス・メンバを使えない?
■2.staticなクラス・メンバは、個々のオブジェクトでも使用できる?
■checkAccountId()とcheckPassword()はクラスメソッド


皆さん、こんにちは

Mr.Hackです。いかがお過ごしですか?最近キーボードを英語版に変えたのですが、日本語の平仮名が表示されていなくて、すこぶるいいですね。僕は、ローマ字うちをしているので、日本語表記はいらないのです。そのキーボード交換についてちょっと感じたことをお話しします。

■米国IBM事情
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

僕は、IBMの回し者ではないのですが、現在のIBM Thinkpadは、かれこれ連続4代目になります。日本IBMのパソコンのサポートは他社に比べて、『かなりよい』というので評判です(http://www.thinkpad-club.com/)。たとえば、キーボードを英語版にしたいときは、日本IBMのWebサイトに行き(http://www-6.ibm.com/jp/pc/home/manual/thinkpad.html)、PDF版の保守マニュアル(Thinkpadの組み立て方、分解の仕方、部品番号が書いてある)から部品番号を調べ、部品センターなるところに電話して注文すればいいのです。そうすると注文票がFAXで送られてきて、銀行振り込みすると1週間ぐらいで英語版のキーボードが届きます。が、しかし、通常は、日本IBMのWebサイトから調べても『部品センター』なる電話番号はわかりません。つまり、一般のエンドユーザーが対象の『部品センター』ではないのです(代理店、パソコンショップ等が対象なのでしょう)。でも、キーボードを変えられない訳ではありません。

一方で米国IBMはどうかと言いますと、それが、『部品センター』に当たる電話番号(IBM Boulder Parts Center)がWEB上に公表されているのです(http://www.pc.ibm.com/qtechinfo/MIGR-4TQVCK.html?lang=en_US&page=brand&brand=IBM+ThinkPad&doctype=&subtype=All&up=unknownuser1004106089)。これは、エンドユーザーたる私たちが、英語版のキーボードに変えたいと思えば誰でも、電話をして型番を伝え(ただし英語で会話しなければなりません。当たり前ですか・・・)、クレジットカードで決済をすると数日で手元に、英語版のキーボードが厳重に包装され、届きます。これはすごいと思いませんか?極端な話をすれば、ノートの液晶を変えたいと思って、変えることができるのです。欲を言いますと、Web上でamazon.comのように注文できたらサイコーなのですが・・・。

そんなこんなで、簡単にパソコンのパーツを買える米国IBMはすごいなーと思いました。

それでは、今週もがんばっていきましょう。今回は、クラスメソッドについて勉強します。

■BankAccount.checkAccountId()とaccount.checkAccountId()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
vol.017で、クラスメソッド(スタティックメソッド)なるものが出てきましたね。覚えていますか?クラスメソッドとは、メソッド(method)にスタティック(static)というキーワードがつくメソッドのことを言いました。checkAccountId()メソッドとcheckPassword()メソッドにはそれぞれ、staticキーワードがついて、メソッドの定義は、

┌──────────────────────────────────┐
static boolean checkAccountId(String accountId);
static boolean checkPassword(String password);
└──────────────────────────────────┘

となりました。staticのキーワードがつくと、クラス名BankAccountに"."(ドット)をもちいて、メソッドをいきなり適用できました。

┌──────────────────────────────────┐
BankAccount.checkAccountId("MrHack"); (1)
└──────────────────────────────────┘

という使い方ができます。一方で、static ではないメソッドの場合は、いったん
インスタンスを生成して、そしてそのインスタンスの参照変数に、"."(ドット)をもちいて、メソッドを適用しました。例えば、checkAccountId()メソッドの定義が、

┌──────────────────────────────────┐
boolean checkAccountId(String accountId);
└──────────────────────────────────┘

とすると、accountという参照変数を作って、

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

としました。(1)と(2)は同じくcheckAccountId()メソッドを使って"MrHack"という文字列をチェックするのですが、いったい、どこが違うのでしょうか?それとも同じことをしているのでしょうか?これを理解するためには、クラスのメンバという考えを知らなければなりません。

■クラス”の”メンバ ( members of a class)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

クラスのメンバ(member of a class)には、2種類あります。フィールド(field)とメソッド(method)です。厳密に言いますと、Java1.1以来、クラス(a class)は他のクラスを含むことができますので(inner classes)、メンバには、classも含まれますが、ここでは省略します。ここでは、メンバとは、フィールドとメソッドのことを言います。また、メンバは二種類の分類があります。一つは、クラス自体に関連のあるメンバで、クラス・メンバ(class members)、または、スタティック・メンバ(static members)といいます。もう一つは、各々のインスタンスに関連のあるメンバで、インスタンス・メンバ(instance members)といいます。クラス・メンバとインスタンス・メンバそれぞれに、フィールドとメソッドがありますので、下記のクラス”の”メンバは下記の4種類あります(クラスのメンバ(members of a class)は、メンバ・クラス(class members)と区別していることに注意)。

┌──────────────────────────────────┐
クラス・フィールド(class fields)
クラス・メソッド(class methods)
インスタンス・メソッド(instance fields)
インスタンス・メソッド(instance methods)
└──────────────────────────────────┘

■Temperature.java
━━━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━
/**
* クラスのメンバの例。気温(単位は摂氏)を求めるクラス。
* convertToFahrenheit()とgetFahrenheit()メソッドの違いに注目。
*
* @since 1.0821
* @author Mr.Hack
*/
public class Temperature {

// クラス・フィールド
public static final double FREEZING_POINT = 0;

// クラス・メソッド
public static double convertToFahrenheit(double celsius) {
return celsius * 9 / 5 + 32;
}

// インスタンス・フィールド
public double temperature;

// インスタンス・メソッド
public double getFahrenheit() {
return temperature * 9 / 5 + 32;
}
}
━━━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━

上記のクラスは、気温(単位は摂氏)のクラスです。これを例にとって4種類のメンバをみてみましょう。

■インスタンス・フィールド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

インスタンス・フィールドとは、staticがつかないフィールドを言います。staticがつかないフィールドは全てインスタンス・フィールドといいます。これは、名前の通り、一つのインスタンス(オブジェクト)に深く関係のあるフィールドです。
たいやきを思い出してください。クラスという設計図作られたオブジェクトは、ちょうど、鉄の型(クラス)からできる鯛焼き(オブジェクト)と同じだと以前お話ししました。インスタンス・フィールドはその中身の『あんこ』をいいました。あんこは、人間によって一つ一つ作られるので、鯛焼きにあんこを入れる時、あんこの量が微妙に違う場合があるでしょう。または、機械づくりで、一つ一つ全く同じ量のあんこが含まれているかもしれません。ここで重要なのは、一つ一つの個性・特徴です。僕の鯛焼きのあんこと、あなたの鯛焼きのあんこが、たとえ機会作りで同じ量だけ含まれていたとしても、全く別物なのです。あなたが買った鯛焼きに含まれるあんこは、鯛焼きを買ったのと同じく、あなたの所有物です。つまり、あなたが買った鯛焼きに深く関係のあるあんこは、僕のあんこでも、第三者のあんこでもなく、あなたが買った鯛焼きに含まれるあんこなのです。このように、インスタンス一つ一つに関連づけられたものが、インスタンス・フィールドです。

上記のTemperatureクラスの例でいけば、東京の気温と、ハワイの気温は違うでしょう。それは、違っていいのです。それをインスタンス・フィールドで表しますと、

┌──────────────────────────────────┐
Temperature tokyo = new Temperature();
Temperature hawaii = new Temperature();
tokyo.temperature = 31.0; //tokyoのtemperatureに31.0を代入
hawaii.temperature = 23.5; //hawaiiのtemperatureに23.5を代入
└──────────────────────────────────┘

となります。もちろん、同じ気温をそれぞれ、tokyo.temperature、hawaii.temperatureにいれてもいいですね。

■インスタンス・メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

インスタンス・メソッドも、staticがつかないメソッドのことを言います。考え方も先ほどの鯛焼きの例と同じです。ただ、C(C++でもCライクな)プログラマの方は注意が必要なのです。OOP(Object Oriented Programming)の醍醐味は、データ中心の考え方です。ここでデータとはインスタンス・フィールドのことを指します。手続き型(プロシジャー型)プログラムはファンクション中心でしたが、JavaのようなOOPはあくまでデータ中心です。そのデータ(インスタンス・フィールド)に働きかける・補助をするものがメソッドなのです。ですから、インスタンス・フィールドに働きかけるのが、基本です。

Temperatureクラスを見てください。インスタンス・メソッドであるgetFahrenheit()メソッドは、temperatureというインスタンス・フィールドを華氏(fahrenheit)に直して(働きかけて)、戻り値を返します。tokyoとhawaiiのそれぞれのインスタンスから、華氏の気温を得るには、

┌──────────────────────────────────┐
tokyo.getFahrenheit();
hawaii.getFahrenheit();
└──────────────────────────────────┘

としますね。getFahrenheit()メソッドは、それぞれのインスタンスのインスタンス・フィールド、temperatureに働きかけ、華氏に変換して、それぞれ違った値をはじき出します。

■クラス・フィールド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

クラス・フィールドは、staticがつくフィールドのことを言います。staticがつきますのでスタティック・フィールド(static fields)ともいいます。

クラス・メンバ(fieldsとmethods)はクラスの特質・特性に深く関係しているメンバです。逆に言いますと、一つ一つのインスタンスには直接関係していない、一般的な特性に関係しているもの、インスタンスすべてに共通するもの、ということが出来ます。すべてのインスタンス共通しているものですから、クラスの特性に関連した働きをする、ユーティリティー的な役割もします。

例えば、鯛焼きの例を挙げますと、クラスフィールドは鯛焼きの表面の形を表します。鯛焼きを作る鉄の型の表面は、尾ひれの形や、目の形、胴体等が刻み込まれています。そこに液体を流し込むと、型に沿って流れこみます。この鯛焼きの表面の形は、僕の鯛焼きであっても、あなたの鯛焼きであっても、全く同じです。尾ひれが何本刻み込まれているとか、目玉が両面一つずつあるとか言うことは、全く同じですね。これは、一つ一つの鯛焼き自体にある特性(あなたが買った一つの鯛焼きの特性)というよりも、鉄の型の特性(鯛焼き一般の特性)が一つ一つの鯛焼き(あなたが買った鯛焼き)に反映されたもの、といった方が適していますね。言い換えれば、鯛焼きであるインスタンスも、鉄の型であるクラスの特性に起因しているということができます。その場合に、わざわざ、一つ一つの鯛焼き(インスタンス)を作らなくても、鉄の型(クラス)から、尾ひれの数とか、目玉が二つあること(クラスの性質・特性)は分かります。これが、クラス・メンバです。

ですから、Temperatureクラスの特性である、staticキーワードがついた氷点の温度は、

┌──────────────────────────────────┐
Temperature tokyo = new Temperature();
tokyo.FREEZING_POINT;
└──────────────────────────────────┘

というように、わざわざインスタンス(tokyo)を作らなくても、Temperatureのクラスの特性から、下記のようにゼロ度である分かるのです。

┌──────────────────────────────────┐
Temperature.FREEZING_POINT; // これで氷点ゼロ度を得ることができる
└──────────────────────────────────┘

static キーワードはよく final キーワードと一緒に使われることがあります。
final はいったん値を設定すると、それ以降値を変更できないようにするキーワ
ドですね。そのようなコンスタントな値は、変数(variable)と区別するために
全て大文字で表記し、単語と単語の間はアンダーバー("_")で表記します。
例で言えば、FREEZINGという単語とPOINTと単語の間に"_"をいれます。これで、FREEZING_POINTは一目で変更不可能な値と言うことが分かります。

■クラス・メソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

クラス・メソッドも、クラス・フィールドのように、staticキーワードがついたメソッドのことを言います。

クラス・メソッドは、先ほどの鯛焼きの例のように、一つ一つのインスタンスの特性に関連があるよりも、むしろ、クラス自体(一般)の特性に関連づけられています。クラスは、インスタンスを生成する設計図ですので、クラス・メソッドは、目が2つあるとか、ひれが何本あるとか言う設計図(クラス)の特性に関して働きかけます。つまり、一つ一つの具体的なインスタンス自身の、個別な特性よりも、一般的なクラスの特性(一般的な鯛焼きの特性)に働きかける、ユーティリティー的な側面があります。鯛焼きの例で言えば、目が二個あるとか、ひれが何本あるというのは、鯛焼きであるが故の特性です。その情報を知るためのメソッド(ユーティリティ)がクラス・メソッドなのです。

Temperatureクラスでは、単位が摂氏である気温を華氏に変える、というユーティリティの役目をしているのが、convertToFahrenheit()メソッドです。これは、インスタンス一つ一つに関連づけられた(影響をあたえる)ものではありまんので

┌──────────────────────────────────┐
Temperature tokyo = new Temperature();
tokyo.convertToCelsius(30.0);
└──────────────────────────────────┘

と、わざわざTemperatureのインスタンス(tokyo)を生成しなくても、

┌──────────────────────────────────┐
double fahrenheit = Temperature.convertToCelsius(30.0);
└──────────────────────────────────┘

と、直接クラスにメソッドを当てはめることができるのです。

Cプログラマの方がいらっしゃれば、Cのfunctionは、Javaではstaticなメソッドが当たります。といいますのも、Temperature.converToCelsius()とすれば、どこのクラスでも使うことができるからです(ただし、そのクラスはインポート(import)しなければなりませんが)。

staticがつくクラス・メンバで注意しなければならないことは、二つあります。

┌──────────────────────────────────┐
1.staticなメソッドからは、インスタンス・メンバを使用できない。
2.staticなクラス・メンバは、個々のオブジェクトでも使用できる。
└──────────────────────────────────┘

■1.staticなメソッドからは、インスタンス・メンバを使えない?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

staticがつくメソッドは、クラス・メソッドで、ユーティリティ的な役割をすると述べました。これは、逆に言いますと、ユーティリティ的なものしかメソッドにできないということを意味します。インスタンス・メンバ(フィールド、メソッド)に働きかけたり、影響を与えることはできないのです。

例えば、Temperatureクラスで、新しく

┌──────────────────────────────────┐
static double converToFahrenheit() {
return temperature * 9 / 5 + 32;
}
└──────────────────────────────────┘

というメソッドは作成できません。これはコンパイルエラーになります(TemperatureクラスのconvertToFahrenheit()のcelsiusをtemperatureに変えてみてください)。どこがいけないのかと言いますと、temperatureというインスタンス・フィールドを使っていることが原因です。インスタンス・フィールドをふくむインスタンス・メンバは、個々のインスタンスに関連づけられたものですね。個々によって値が変わる(可能性がある)temperatureインスタンス・フィールドは、convertToFahrenheit()メソッド(一般的な気温に関連づけられたクラス・メソッド)で、華氏を得ることができません。なぜなら、個々のtemperatureによって、一般的な気温の華氏が変わってしまうことは許されないからです。

技術的に言いますと、クラス・メンバは、インスタンス(インスタンス・メンバ)が生成される前に作られますので、まだ、生成されていないインスタンス・メンバを、クラス・メンバから使うことができないのです。

それでは、staticなフィールド、FREEZING_POINTをつかったgetFreezingPointWithFahrenheit()はどうでしょうか?氷点を華氏で求めるメソッドです。

┌──────────────────────────────────┐
static double getFreezingPointWithFahrenheit() {
return FREEZING_POINT * 9 / 5 + 32;
}
└──────────────────────────────────┘

FREEZING_POINTは、摂氏でゼロ度を表しますので、これを華氏に変換公式を適用してあげれば、単位が華氏の氷点が求まりますね。これは、可能です。なぜなら、FREEZING_POINTは、インスタンス・メンバではなく、staticキーワードがついたクラス・メンバだからです。FREEZING_POINTという氷点は、すべてに(すべてのインスタンス)共通することですからね。

■2.staticなクラス・メンバは、個々のオブジェクトでも使用できる?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

それでは、逆に、staticキーワードがついたフィールド・メソッドは、個々のインスタンスで使用できるのでしょうか?答えは、『できます』。

普通は、staticなフィールド・メソッドである、FREEZING_POINT、convertToFahrenheit()メソッドは、それぞれ、

┌──────────────────────────────────┐
double freezingPoint = Temperature.FREEZING_POINT;
//30.0の単位は摂氏
double fahrenheit = Temperature.convertToCelsius(30.0);
└──────────────────────────────────┘

と使用しますが、それぞれ、

┌──────────────────────────────────┐
Temperature tokyo = new Temperature();
//個々のオブジェクトにsaticなフィールド・メソッドを適用
double freezingPoint = tokyo.FREEZING_POINT;
double fahrenheit = tokyo.convertToCelsius(30.0);
└──────────────────────────────────┘

とできます。これは鯛焼きの例で言いますと、鉄板の方から目の数や、ひれの数がわかりますが、同様に、僕の鯛焼きや、あなたの鯛焼きからも、目の数や、ひれの数がわかることを意味します。そう考えますと、ここの気温である、一般的な氷点を求めるのに、tokyoというインスタンスからFREEZING_POINTである氷点がわかったりするのも、別に変なことではありませんね。

ただ、先ほども申しましたように、staticなクラス・メンバはクラス自体の特性・性質に関連づけられたもの(すべてのインスタンスに共通するもの)ですので、わざわざ、個々のインスタンスを生成してから、それにフィールドやメソッドを適用する必要はないと思います。

■checkAccountId()とcheckPassword()はクラスメソッド
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

それでは、今回習ったことをBankAccountクラスのcheckAccountId()とcheckPassword()メソッドに応用してみましょう。両方のメソッド定義は、

┌──────────────────────────────────┐
boolean checkAccountId(String accountId);
boolean checkPassword(String password);
└──────────────────────────────────┘

でしたね。ある文字列を引数として受け取ると、その文字列が適切な文字列かをチェックし、適切な文字列であればboolean型のtrueを、そうでなければfalseを返すメソッドです。

これらは、インスタンス・メソッド(インスタンスに関連づけられたメソッド)がいいのでしょうか?それとも、クラス・メソッド(クラスに関連づけられたメソッド)がいいのでしょうか?

僕は、staticキーワードをつけてクラス・メソッドにするべきだと思います。といいますのは、

┌──────────────────────────────────┐
インスタンス・フィールドのaccountIdとpasswordや、その他のフィールド
に影響を及ぼすメソッドではないこと
└──────────────────────────────────┘

が理由です。つまり、一つ一つのBankAccountクラスのインスタンスのaccountIdやpasswordに対しては、何も働きかけていません。上記のメソッドがしているのは、クライアントが渡した引数をチェックして、適切かどうかをみているだけです。オブジェクト(のフィールド)には何も影響を及ぼしていませんね(例えば、すでに存在するIDやパスワードと、コンソール上から入力されたIDとパスワードを比較したりするわけではなく、コンソール上から入力されたIDとパスワードをチェックするだけのメソッド)。ですから、staticキーワードをつけたのです。

staticキーワードがついたクラス・メンバは、インスタンス生成の前にすでに存在していますから、インスタンス・メンバ(フィールド・メソッド)から参照できます。そのため、setAccountId(String accountId)メソッドや、setPassword(String password)メソッドから使用して、

┌──────────────────────────────────┐
public void setAccountId(String accountId) {
//staticなcheckAccountInd()は、インスタンス・メソッドのsetAccountId()
//を使用する時には、すでに存在。
if (!BankAccount.checkAccountId(accountId)) {
throw new IllegalArgumentException(
"アカウントIDが正しくありません。");
}
this.accountId = accountId;
}
└──────────────────────────────────┘

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

とするともできましたし、

BankAccountクラスを使用するBankUserInterfaceのようなクライアント(クラス)からは、

┌──────────────────────────────────┐
if (BankAccount.checkPassword(password)) {
setPassword(password);
}
...
└──────────────────────────────────┘

というように使用できます。

今回は、インスタンス・メンバ、クラス・メンバを勉強しました。staticキーワードをつけることによって、フィールド、クラスの役割が変わってきました。staticがつくと、インスタンス共通の事柄を表すこと、ユーティリティー的な役割をになうことを勉強しました。この部分は、初心者の方は、一読しただけでは理解しずらいと思います。3回は読んでくださいね。念を押しますが、机に座って、最低3回は読まないとわからないとおもいますよ。

次回は、BankUserInterfaceクラスを修正していきます。

それでは、また、会いましょう。

Mr.Hack

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


© 2002 MR.HACK ALL RIGHTS RESERVED