クラス!クラス!クラス! 前回の連載では,クラスがOOP(Object Oriented Programming,オブジェクト指向プログラミング)の基本であることを説明しました。クラスとは,関数と変数を一つのグループにまとめて名前を付けたものです。クラスによってOOPが実現されます。JavaやC#などのOOP言語にはクラスを定義する構文があり,C言語やFORTRANなどの非OOP言語にはありません。ただし,OOP言語と非OOP言語の違いは,クラスを定義できるかどうかだけではないのです。

 OOP言語は,クラスを対象として,便利な機能を数多く提供しています。その中で特に注目してほしいのが「継承」「カプセル化」「多態性」という三つの機能です。これらは,「OOPの三本柱」と呼ばれ,OOPのメリットを増強するものです。今回は,OOPの三本柱の機能と活用方法を説明します。皆さんに「OOPって面白いなぁ,クラスって便利だなぁ」と感じていただければ幸いです。それでは,始めましょう!

OOPの必須機能と補足機能

 筆者のこれまでの著作では,非OOP言語の解説は1冊に収まりましたが,OOP言語の解説書は2分冊になってしまいました。OOP言語は,非OOP言語が持っている機能(変数,関数,演算子,制御構造など)に,OOPを実現するための機能(クラス,継承,カプセル化,多態性など)を付加したものだからです。OOP言語をマスターするには,たっぷりと時間をかけて多くの機能を学習しなければなりません。ただし,せっかくOOP言語の学習をしたのに,思うようにプログラミングができないと嘆いている人もよくいます。多くの知識にほんろうされてしまい,どの機能を使えばよいのか迷ってしまうようです。

 皆さんがOOPの大量の知識に惑わされないように,まず,OOPに必須の機能と,補足的な機能を明らかにしておきましょう。「これだけは絶対にやらなければダメ」という必須の機能は,クラスを作り,クラスのメンバー(関数と変数)を使うことです。これは,非OOP言語のプログラミング・スタイルに,変数と関数をクラスでグループ化するという概念が加わっただけです。簡単なことですから,だれでも理解でき,実践できるでしょう。どうぞ安心してください。

図1●クラスを土台としてOOPの三本柱がある
図2●OOPの三本柱を活用するポイントは“思いやり”
リスト1●クラスの使われ方は3 種類ある
図3●リスト1の実行結果
図4●継承はプログラミングを効率化する
図5●汎化と継承の関係

 これに対して,OOPの三本柱はちょっと難しくなります。しかし実は,「必要なら使ってください。別に使わなくても構いませんよ」という程度の補足的な機能なのです。OOP言語のさまざまな機能の関係は,クラスを土台として,その上に継承,カプセル化,多態性が柱のように立っているようなイメージになります。だから,継承,カプセル化,多態性のことを「OOPの三本柱」と呼ぶのです。OOPの三本柱は,どれもクラスを対象とした機能です(図1)。

【ここまでのまとめ】
・クラスを作ってクラスのメンバーを使うことは,OOPに必須の機能である。
・OOPの三本柱(継承,カプセル化,多態性)は,OOPの補足的な機能である。

OOPの三本柱を適切に活用するポイント

 OOPのメリットは「クラスを再利用できるのでプログラミングが効率化すること」と,「クラス単位で改造や修正を行えるので保守性が向上すること」の二つです。プログラミングにおいてOOPの三本柱を活用する目的も,もちろん効率化と保守性の向上です。

 ただし,不適切な場面でOOPの三本柱を使うと逆効果になってしまう場合があります…。あれあれ,こんな風に説明すると,OOPの三本柱を使うのが怖くなってしまいますね。そこで皆さんに,OOPの三本柱を適切に活用するための,とっておきのポイントをお教えしましょう。それは技術的なことではありません。OOPを実践するときの心構えは,「思いやり」を持つことです。思いやり!思いやり!思いやり! と何度も強調したいほど重要なポイントです。

 OOPは,大規模なプログラムに適したプログラミング・スタイルです。大規模なプログラムは,複数のプログラマから編成されたチームによって開発されます。チームの中では「私はクラスを作る人,あなたはクラスを使う人」という役割分担ができてきます。皆さんは,クラスを作る人になったとしましょう。どんなクラスを作りますか? 必要な機能を満たしたクラスを作る――もちろん正解です。ただし,それだけではプロの仕事として十分ではありません。使いやすいクラスを作ることを心掛けるべきです。すなわち,クラスを使う人への思いやりを込めたクラスを作るのです。

 OOPの三本柱である継承,カプセル化,多態性は,どれもクラスを使いやすくするための機能と言えます。ただし,これらの機能を技術的に理解しただけでは,適切な場面で効果的に活用することはできません。思いやりという観点から個々の機能を理解すれば,自然に使いこなせるようになります(図2[拡大表示])。

 思いやりを込めたクラスを作るためには,クラスの使われ方を知らなければなりません。クラスの使われ方には,(1)クラスのメンバーを使う,(2)クラス全体を継承して使う,(3)別のクラスのメンバーとして使う,があります。皆さんが作るクラスは,同じチームのほかのプログラマ(おそらく皆さんの先輩でしょう)に使われるはずです。プログラミングを始める前に,チーム内でよく相談して,皆さんが担当するクラスがどのような使われ方をするかを確認してください。そのうえで,使いやすいクラスを作るのです。

 クラスの3種類の使われ方を示すサンプルをお見せしましょう(このプログラムの内容に意味はありません)。リスト1[拡大表示]は,Javaで記述したアプレットの一部です。アプレットとは,Webブラウザ上で動作するプログラムのことです。このプログラムは,全体がMyClassというクラスになっています。MyClass extends Appletの部分は,Appletクラス全体を継承したMyClassクラスを定義することを意味しています。これがクラスの使われ方の(2)です。継承の意味に関しては,すぐ後で説明します。

 MyClassクラスのメンバー(「{」と「}」の中に記述された変数と関数)として,btという名前の変数(フィールド)と,initおよびpaintという名前の関数(メソッド)があります。btのデータ型は,ボタンの機能を提供するButtonクラスです。MyClassのメンバーにButtonクラス全体を含めているわけです。これがクラスの使われ方の(3)です。

 paintメソッドに記述した処理内容は,GraphicsクラスのメンバーであるdrawStringメソッドを使って「日経ソフトウエア」という文字列を表示することです。これがクラスの使われ方の(1)です。プログラムの内容を細かく理解する必要はありません。雰囲気を感じていただければOKです。プログラムの実行結果は図3のようになります。

【ここまでのまとめ】
・クラスを作る人は,クラスを使う人への思いやりを持つことが重要である。
・クラスには,大きく分けて3種類の使われ方がある。

継承とは何だろう

 ちょっと前置きが長かったですが,ここから先は,いよいよOOPの三本柱の機能を順番に説明して行きましょう。まず「継承(inheritance,インヘリタンス)」からです。継承とは,だれかが作った既存のクラスの内容を引き継いで,新たな別のクラスを定義することです。先ほどのリスト1では,Appletクラスを継承してMyClassクラスを定義しました。これによって,MyClassクラスの「{」と「}」の中に何も記述しなくても,Appletクラスで定義されているすべてのメンバーがMyClassクラスに引き継がれます。つまり,もしもMyClassクラスのメンバーが空っぽでも,「{」と「}」の中にはAppletクラスのすべてのメンバーが記述されているのと同じことになるのです。extendsは,継承を表すJavaのキーワードです(図4[拡大表示])。

 Appletクラスを作った人(Javaの開発元である米Sun Microsystemsのプログラマでしょう)は,実に思いやりのある人です。どのようなアプレットを作る場合でも,必ず必要な機能(メンバー)をAppletクラスに定義しておいてくれたのです。アプレットを作る人は,自分のクラスにAppletクラスをありがたく継承させてもらえば,それだけでWebブラウザ上でプログラムを動作させるのに必要なメンバーがそろってしまうのです。後は,自分のクラスに必要な独自のメンバーを追加するだけです。

 Javaでは,Appletクラスのように継承元となるクラスを「スーパークラス(superclass)」と呼び,MyClassクラスのように継承先となるクラスを「サブクラス(subclass)」と呼びます。スーパークラスはさまざまなサブクラスの共通点を抽出したものだと言えます(これを「汎化」と呼びます)。汎化したからこそ継承が実現できるのです(図5[拡大表示])。

 継承が,どうしてプログラミングを効率化するのか,どうしてプログラムの保守性を向上させるのかがわかりますか?それは,「MyClass extends Applet」と記述するだけで,MyClassクラスの中にAppletクラスのすべてのメンバーを記述したのと同じことになるからです。コーディング量が大幅に減って効率的です。もしもAppletクラスにバグがあったら,Appletクラスだけを修正すれば,それを継承しているすべてのクラスを修正したことになります。保守も容易なのです。

【ここまでのまとめ】
・継承とは,既存のスーパークラスを引き継いで,新たなサブクラスを定義することである。
・スーパークラスは,複数のサブクラスの共通点を抽出したものである。