GO BACK

初心者のためのJava教室 その7

「基礎知識再び」

■オブジェクト指向の考え方

 怒涛のような勢いで、とりあえずウィンドウからボタンまで作ってしまいましたが、とにかく何か形のあるものを作れるようにしようと思い、細かな説明をかなり省いて進んでしまいました。とりあえずGUIまでいったことだし、ちょっと一息入れて、もう一度Javaの基本に立ち返って考えることにしましょう。

 今までざっと触って、「Javaってのはなんか変わった言語だな」と感じたことでしょう。クラスというもの、その中につまっているメソッド、クラスを変数の中に生成したり、コンポーネントってクラスに別のクラスを入れたり…。なんだかわかったようなわからないようなプログラムの作り方をしますね。

 そもそも、クラスとはなんでしょう?−−クラスというのは「設計図だ」といいました。これはおおまかに特徴をいってはいますが、正確な表現ではありません。

 クラスというのは、「あるオブジェクトの働きを定義したものである」といえます。オブジェクトというのは、「それ自身に必要な手続きと情報が全てまとめられたもの」です。よく「オブジェクト指向」なんて言葉を耳にしますが、オブジェクトというのはそういうもののことです。

 例えば、今まで作ったクラスを考えて見てください。クラスには、自分自身を定義するメソッドがありました。そしてそのクラスに必要なメソッドが全てまとめて書かれていました。このクラスで実行できることは、全てこのクラス自身の中にまとめてあったわけです。こういうのを「オブジェクティブである」といいます。

 クラスは、汎用性の高い機能を1つ1つメソッドという形で定義しまとめたものです。実際にプログラムの中でこのクラスというものを使うときは、newというのを使い、クラスを参照する変数を作ってそれを操作していました。今まで単に「変数」と呼んでいましたが、このようにクラスを元にして、実際に生成されたオブジェクトを「インスタンス」と呼ぶ、と説明しました。また、クラスからインスタンスを生成することを「インスタンス化」(インスタンシエーション)といったりもします。

 newされると、Javaは仮想マシンのメモリ内にクラスをロードし、インスタンスを作ります。そしてそのインスタンスを変数に設定するのです。こうやって「クラスを元にインスタンスを作る」という作業をしないと、そのクラスは使えないのです。

 クラスとインスタンスは、ちょうどステーショナリファイル(ひな形)を連想させます。ステーショナリというのは、よく使うフォーマットを保存した特別な形式のファイルですね。このファイルに、必要なフォーマットの情報が既に組み込まれてあります。これを使うときは、ステーショナリを開くと、自動的にその複製ができ、それに修正を重ねます。クラスとインスタンスもこれと同じです。クラスを使うときは、必ずnewでインスタンスをメモリ内に生成し、このインスタンスを操作します。

 インスタンスは、そのクラスの性質を全て保持しています。そしてそれ以外に、そのインスタンス独自のものなども付け足すことができたりします。同じクラスから作られたインスタンスでも、その後の操作によっては違うものになる可能性もあるのです。

 それぞれのクラスの中には、そのクラスに用意されているさまざまな機能が記述されています。これが、お馴染み「メソッド」です。

 メソッドの中には、必ず「そのクラスをどう作るか、クラスの初期化を定義するメソッド」がありました。クラス名と同じ名前のメソッドがあって、そこにクラスを作る時の定義を書いていたことを覚えていますか? この「クラスを生成するための手続きを書いたメソッド」のことを「コンストラクタ」といいます。クラスのインスタンスを生成するときは、このコンストラクタをnewで呼び出していたのです。

 さて、クラスの中には、更に別のクラスを継承して作られたものもありました。先に作成したTest4クラスも、Frameクラスを継承していましたね。このような継承関係にあったとき、「Test4はFrameのサブクラスである」といいます。また逆にFrameのほうから見た場合には、「FrameはTest4のスーパークラスである」といいます。

 先に、クラスの定義を行なうときに「super ()」というのを使って、上位にあるものへ処理を渡していましたね。これはつまり「スーパークラスに渡していた」のです。それでsuper ()という名前だったのですね。

 Javaプログラムの基本的な構造と考え方がだいたい頭に入ったでしょうか。


■Javaの変数について

 次に、変数についてもう少し理解を深めることにしましょう。とりあえず今までは「int」「String」という型だけ使ってきましたが、他にもたくさん型があります。


「byte」−−8bitの大きさの大きさの整数。つまり-128〜127までしか使えない。

「short」−−16bitの大きさ。-32868〜32767まで使える。

「int」−−32bitの大きさ。2 ^ 32乗を計算して、半分がマイナス(笑)。

「long」−−64bitの大きさ。intの倍だね。

「float」−−32bitの大きさ。ただしこちらは浮動小数が扱える。

「double」−−64bitの大きさの浮動小数。floatの倍だ。

「char」−−16bitの大きさ。1つのキャラクタ(文字)を入れておける。

「boolean」−−1bit。真偽を示す値。true/falseのいずれかの値になる。


 特に数字に関係するものがたくさんありますが、こんなにたくさんある理由は、変数が使用するメモリサイズによる違いと考えてください。特に大きな数字を使う必要がなければ、メモリを節約することだけでなく、小さな変数を利用することでプログラムの速度が向上するのです。

 けれど、現段階ではそこまで細かく考える必要はないでしょう。それに、変数の型が違うと、他の型の値と計算するときに型変換など面倒な操作が増えますから、特に問題がなければ、「数字ではint、文字列はString」と考えてしまってよいでしょう。

 変数には、この他に「配列」というものもあります。これは変数の型というより、上に示した型の値をずらっと並べて一つに収めておける特別な入れ物だと考えればいいでしょう。この配列は、


《値の型》 [] 《配列名》;


このようにして定義します。そして実際に配列を作る時には、newを使い、


《配列名》 = new 《値の型》 [《数》];


このようにします。しかし、いちいち分けて書くのは面倒ですから、通常は1つにまとめて、


《値の型》 [] 《配列名》 = new 《値の型》 [《数》];


このように書くのが普通です。−−ちょっとわかりにくいかな?

 では、例を上げましょう。例えば「int型の値を5つもてるような配列を作りたい」と思ったとしましょう。すると、


int [] hako = new int [5];


このようにすると配列「hako」が作成されるというわけです。作った配列の中の値を取り出すときは、例えば「hako [3]」というようにします。これで3番目の値が取り出せるというわけです。

 まあ、配列というのは必要性が出てくるまでは深く考えなくてもいいでしょう。「そういう機能もある」とだけ頭に入れておけば十分です。


■クラス定義を再考する

 さて次に、今まであまり深く考えてこなかった、クラスやメソッドの定義について、もう少し理解を深めることにしましょう。まずはクラスの定義からです。クラス定義は、このような形で記述します。


【《修飾子》】 class 《クラス名》 【extends 《クラス》】【implements 《インターフェイス》】


 以上のうちの【】で囲まれた部分はオプションで、使わないこともあります。

 まず「修飾子」というものから。これはクラスや、メソッド、クラスにおいてある変数なんかでも使われます。けっこういろいろあるんですが、以下のものぐらい知っておきましょう。


「public」−−これは、「このクラスは一般公開します」というときに使います。これを使うと、そのクラスは他のパッケージなどから自由に呼び出せるようになります。これを指定しないと、そのクラスのあるパッケージ内からしか呼び出せません。

「private」――これは、同じパッケージからしか使えないってときのものです。まあクラスだとわからないでしょうが、メソッドでは「クラスの外から使えない」ってことになります。

「protected」――これはprivateとpublicの間みたいなもんで、同じパッケージとサブクラスから使えます。要するに「仲間内だけ使える」ってものですね。

「final」−−これは、避妊宣言です(笑)。これを使うと、もうこのクラスの子供は作れなくなります。つまり、継承してサブクラスを作れなくなるのです。

「abstract」−−これは「抽象クラス」という宣言に使うものです。抽象クラスというのは難しいんですが、「実体化されないクラス」と考えてください。つまり、インスタンスを生成できないクラスなのです。


 それから「extends」「implements」というのもありますね。「extends」は既に登場しました。あるクラスを継承したサブクラスとしてクラスを定義したいときに使うものでしたね。

 もう一つの「implements」というのは、イベントリスナーで登場しました。これは「継承できないんだけど使いたいクラスを利用できるようにする」というものでしたね。例えば、「A」というクラスを継承したクラスを定義するのだけど、その中でどうしても、全く「A」とは関係のない「B」というクラスの機能を使いたい、と思ったとしましょう。extendsによる継承は、1つのクラスからしか継承できません。そこで、


class X extends A implements 《Bのインターフェイス》


このようにクラス定義を行なうことで、Aを継承しながらBの機能も使えるようにすることができるというわけです。このimplementsは、1つだけでなく、カンマで区切っていくつも続けて記述できます。こうすることで、いくつもの機能を全部使えるようになるのです。

 ただし、これには制限があります。「クラスBにインターフェイスと呼ばれるものが用意されていないと利用できない」ということです。インターフェイスというのは、そのクラスの外部とやりとりをするための機能で、クラスによってはこれが用意されているものといないものがあります。

 まあ、implementsについては、イベント処理以外には当分は使わないでしょうから、今は「イベント処理用の機能だ」という程度に考えておけばいいでしょう。

 では、以上を頭に入れて、前に作成したTest5クラスの宣言部分を見てみましょう。これは、このようになっていました。


public class Test5 extends Frame {


 ここでやっと、全ての意味が理解できたわけです。そう、Test5はFrameを継承し、他から自由にアクセスできるpublicなクラスとして宣言されていた、というわけですね。


■メソッド定義を再考する

 では、クラス内にあるメソッドの定義に進みましょう。こちらもクラス定義と似ています。


【《修飾子》】 《値の型》 class 《クラス名》(《パラメータ》) 【throws定義】


 まず一見してわかるのは、「メソッドには値の型がある」ということです。2番目にある《値の型》のことですね。これは文字通り、intとかcharといった、値の型そのもののことです。

 メソッドの中には、呼び出した元へ値を送り返すものがあるのです。関数みたいに、呼び出すと何かの値を返すようなものですね。そのようなメソッドでは、送り返す値の型をここで指定しておくのです。

 では、値を返さないメソッドはどうするのか。−−そのときは「void」というのを指定します。これは、「このメソッドは値を返しません」ということを示す予約語なのです。

 それから、修飾子について。これは、先に上げたものの他にこういうものが付け足されます。

「static」−−これを宣言すると、このメソッドはクラス内に1つだけしか定義されなくなります。つまり、そのクラスから生成された全てのインスタンスやサブクラスで、共通のメソッドが呼び出されるようになるのです。

 さて、最後の「throw定義」というのですが、これはメソッド内に例外処理が発生した場合に処理を送る先を指定するものです。命令がうまくいかなかったときなどの処理に使われます。まあ、現時点では知らなくても大丈夫です。

 では、以上を踏まえて、Test4のmainメソッドを見てみましょう。

public static void main (String args []) {

 これはつまり、このmainがこのクラス唯一のものであり(static)、値を返さず(void)、そして他からも自由に呼び出し可能な(public)ものである、ということを定義しているわけです。そして、パラメータとしてString配列をもっています。ただし、mainは通常クラスが動作したとき自動的に呼び出されるものですから、このString配列のパラメータが使われることはほとんどありません。まあ、おまじないみたいなものだと思ってください。

 さて、これでだいたいはわかりましたが、上のルールに違反(?)しているメソッドが1つだけあります。それは「コンストラクタ」です。コンストラクタは、返す値の型がありません。コンストラクタはnewで自身のインスタンスを生成する特別なものなので、他のメソッドとは違う、別格の扱いを受けているのですね。

 さあ、これで、今までなんとなくもやもやしていた部分が一通り理解できるようになったわけです。−−え、まだよくわからない? そうかも知れませんね。まあ、現段階ではそう深く考えなくとも簡単なプログラムを書く上では不自由はないかもしれません。しかし、いつかもう一歩上のプログラムに挑戦したいと思ったとき、これらの基礎知識はどうしても必要になります。ですから、とりあえずはどこかにテキストを保存しておいて、いつか必要になったときに役立てるようにしてくださいね。



GO NEXT


GO HOME