オブジェクト指向

Javaはオブジェクト指向という考え方に基づいた言語、すなわちオブジェクト指向言語です。Javaでプログラムを書くということは、オブジェクト指向言語という概念を知っておく必要があります。ここでは、オブジェクト指向という考え方そのものに触れていきます。

オブジェクト指向とは 
プログラム言語にはさまざまな種類があります。コンピュータが直接理解できる形の機械語から、機械語をもう少し人向けにアレンジしたアセンブリ言語、VisualBasicなど、数え上げればきりがないですが、こうしたプログラム言語は、いくつかの種類に分類することができます。

Javaはオブジェクト指向言語として位置付けられています。オブジェクト(Object)とは、「物」をあらわす言葉で、簡単に言うとオブジェクト指向とは、「小さなプログラムを部品単位で作成しておき、これらを組み合わせることによって、より大きなアプリケーションを作る」という考え方のことです。

これは、従来のプログラミング手法と比べて非常に便利で、かつ画期的な考え方です。

オブジェクト指向登場以前のプログラミング言語では、プログラムを作成する際、プログラマがすべての処理を常に1から記述していく必要がありました。ところが、オブジェクト指向言語の場合、よく使われる処理はあらかじめ部品のような形でどこかにストックされていて、必要に応じて呼び出して使うことができるようになっています。

たとえば、私たちがいつも目にしているWindowsのアプリケーションには、ボタンやメニューバーなどが組み込まれていますが、非オブジェクト指向言語でこれらの機能を実現しようとした場合、ボタンの形状から、クリックされたときの処理の様子まで、プログラマが逐一コーディングしていく必要があります。ところが、オブジェクト指向の言語では、ウィンドウやボタンなどのように、頻繁に使われるものをあらかじめ「部品」のような形で作成しておき、必要なときにこれを呼び出してプログラムに組み込むことができるようになっています。もちろん、細かい部分については、それなりにコーディングを加えなければなりませんが、これにより、大幅に手間が削減できます。
部品には、色や幅・高さなどの情報(データ)、およびその部品の持つ機能がパッケージされていて、呼び出し側のプログラムで必要な性質を定義してやるだけで、好みの外見を持つ部品のコピーを作成して利用することが可能です。このような情報と機能をパッケージしたものをオブジェクトと呼び、これらのオブジェクトは基本的に何度でも再利用することができます。
クラスとオブジェクト 
クラスとは、オブジェクトの設計図のようなもので、そこには該当オブジェクトがどのような情報や機能を持っているかが、細かく定義されています。Javaでは情報のことをフィールド、機能のことをメソッドと呼びます。C言語でいうと、フィールドは変数に相当し、メソッドは関数に相当します。

一枚の型紙から、色・素材の異なる何枚ものスーツを作ることができるように、1つのクラスからは性質の異なるいくつものオブジェクトを作成することが可能です。この場合、スーツがオブジェクトに相当し、型紙がクラスに相当します。

オブジェクト指向プログラミングでは、クラスに基づいて、オブジェクトを生成することになります。

プログラムの中でオブジェクトを使用するには、型紙であるクラスを元にオブジェクトを作成する必要です。これを、クラスの実体化、もしくは、クラスのインスタンス化といいます。

クラスのインスタンス化にはコンストラクタと呼ばれる特殊なメソッドを用い、これがオブジェクトの初期化処理を行います。
オブジェクト指向プログラミングの3つの原則
ここで、オブジェクト指向プログラミングの3つの原則について触れておきますが、これはJavaやC++などのオブジェクト指向言語をある程度理解した後で、もう一度見直した方が理解しやすいので、はじめてオブジェクト指向言語に触れる人はここでは深く考える必要はありません。
・カプセル化
カプセル化とは、クラスの中にデータを隠蔽する方法と、それらをメソッドを通してのみアクセスできるようにする方法のことです。データと内部メソッドをクラスの安全な「カプセル」の中に閉じ込めて、信頼できるユーザ(クラスのメソッド)にしかアクセスできないようにします。

なぜこのようなことをする必要があるのかというと、最も重要な理由は、クラス内部の細かい実装を隠すためです。プログラマがそうした詳細に依存しないようにすれば、クラスを利用している既存のコードが壊れることを心配せずに、実装を安全に変更できるからです。

カプセル化を行うもう一つの理由は、偶然または故意による間違いからクラスを守るためです。クラスにはしばしば、一貫性を保つ必要のある相互に依存するフィールドが含まれていることがあります。自分を含め、プログラマにそれらのフィールドを直接操作できるようにすると、あるフィールドを変更しておきながら関連する重要なフィールドの変更を忘れて、クラスが一貫性の無い状態になることも考えられます。フィールドの値を変えるために、メソッドを呼び出さなければならないとしたら、そのメソッドが状態の一貫性を保つために必要な処理をすべて行うと考えられます。同様に、クラスが内部での使用を目的としていくつかのメソッドを定義する場合、それらのメソッドを隠すことで、クラスのユーザがそれらを呼び出せないようにすることができます。

カプセル化を別の方向から見てみると、クラスのデータがすべて隠蔽されていると、メソッドはそのクラスのオブジェクトに対して実行できる一連の操作のみを定義します。メソッドを注意深くテストしてデバックすれば、クラスが期待通りに動作することが確認できます。
・継承
Javaで新しいクラスと書くとき、あるクラスを基に新しいクラスを構築することができます(Javaではクラスの定義は常にこの形で行われています)。あるクラスを基に新しいクラスを構築することを、クラスを拡張するといい、クラス拡張の一般的な概念を継承と呼びます。事実Javaには大元となるクラス(基底クラス)が存在し、すべてのクラスはこれをベースに作られます。Javaではすべてのクラスはこの基底クラス(Objectクラスと呼ばれる)を拡張したものです。

たとえば、クラスYがクラスXを継承する場合、Yを「Xのサブクラス」と呼び、Xを「Yのスーパークラス」と呼びます。さらに、ほかのクラスがYを継承することもできます。このようにして、一連のクラスに対して、継承の階層関係が形成されます。

継承においては、スーパークラスの機能をそのまま使うだけではなく、それらを自分なりにアレンジして用いることも可能となっています。スーパークラスの持つメソッドに新しい機能を追加して、「自分なりのメソッドに」アレンジすることを、オーバーライドといいます。



1つのクラスは複数のサブクラスを持つことができます。しかしJavaでは、1つのクラスは直接的なスーパークラスを1つしか持つことができません。Javaの継承階層のルートにあるのがObjectクラスです。

1つのクラスに1つのクラスを継承させることを単一継承といいます。一方、単一継承に対して、1つのクラスに複数のクラスを継承させることを多重継承といいます。多重継承の場合、サブクラスは継承もとの性質をすべてもつことになりますが、Javaでは多重継承はできなくなっています。

多重継承の代わりに、Javaではインターフェイスというクラスを用いて似たようなことを実現します。インターフェイスは基本的に、継承して使うことを目的としたクラスです。

インターフェイスは処理が無い空のメソッドとフィールドからなり、インターフェイスを継承した先のクラスで処理を記述します。
・ポリモーフィズム
世の中に存在するオブジェクトの多様性を効率的に表現するためには継承だけでは不完全です。そこで、インターフェイスと実装をクラス階層中の別の場所に配置する仕組みが必要になります。

たとえば、「犬」「猫」「猿」クラスは「動物」クラスを継承しているとすると、「鳴く」という機能はどうあればいいでしょうか?「鳴く」という行為は「犬」「猫」「猿」どれも似ているが、鳴き声は全く違います。「犬」クラスのインスタンスに「鳴け」と命令した場合(つまり「鳴く」メソッドを実行した場合)は「わん!」と鳴くし、「猫」クラスのインスタンスなら「にゃ!」と鳴くでしょう。したがって、それぞれのクラスについて、共通の目的と独自の挙動をもつメソッドを定義する必要があります。


つまり、インターフェイスは「動物」クラスで定義し、実装は「犬」「猫」「猿」クラスで定義するのが自然です。インターフェイスが「動物」クラスで定義されると、そのサブクラスである「犬」「猫」「猿」クラスのインスタンスを扱うときに、それが実際にどのサブクラスに属するのかを知る必要がなくなります。それがどのクラスのインスタンスであろうと「動物」インスタンスの一種なので、「動物」クラスで定義されている「鳴く」メソッドを共通的に実行できます。そしてインスタンス自身は自分がどのクラスに属しているか知っているので、自分専用の実装を使用して鳴き声を出すことができるのです。

このように、インターフェイスがひとつでも、オブジェクトが属するクラスによって挙動が変わる仕組みのことをポリモーフィズムと言います。この仕組みはオブジェクト指向にとって非常に重要なことです。