やり直しJava

オブジェクト指向プログラミング

Javaはクラスベースのオブジェクト指向言語で クラスとインタフェースがその中心になります。オブジェクト指向の代表的な概念および技法は次の通りです。

  • カプセル化
  • 継承
  • ポリモーフィズム(多態性)
    • アドホック多相
    • パラメータ多相
    • サブタイピング
  • ダイナミックバインディング

それぞれJavaでどのように実現しているかを見て行きます。

カプセル化

カプセル化によりオブジェクト内部のデータを隠蔽したりオブジェクトの振る舞いを隠蔽したりすることができます。Javaではクラスを定義しフィールドやメソッド等へのアクセス制御を行うことでカプセル化を実現しています。

カプセル化によって システムを構成するコンポーネント間の関係が疎になり、開発・テスト・パフォーマンスチューニングなどを 他のコンポーネントに影響を与えることなく独立して行うことができます。

継承

継承により あるオブジェクトが他のオブジェクトの特性を引き継ぐことができます。継承の元になるクラスをスーパークラス、継承によりスーパークラスの特性を引き継いだクラスをサブクラスと呼びます。継承はIs-aの関係となり、サブクラスはスーパークラスの一種であるという意味的な関係が成り立ちます。

サブクラスではスーパークラスに機能を追加したり スーパークラスの機能を部分的に置き換えたりすることができ、コードの再利用性を高めることができます

また、スーパークラスとして宣言された変数にサブクラスのインスタンスを格納することができます(リスコフの置換原則)。スーパークラスを扱う抽象的なコードを書くことにより、実際に格納するインスタンスを別のサブクラスに置き換えることが容易になり クラス間の結合を緩やかにすることができます

Javaではクラスとインタフェースが継承のメカニズムを使用することができます。しかし、クラスとインタフェースは別物で、クラスは主にオブジェクトの属性や振る舞いを定義する役割を果たし、インタフェースは主に型やAPIの定義の役割を果たすため、継承においても次のような違いがあります。

  • クラスは単一継承ですが インタフェースは多重継承が可能です。Java SE8から インタフェースがデフォルト実装を持てるようになったため、インタフェースでは 多重継承特有のいわゆるダイヤモンド問題を考慮する必要が出てきました。
  • インタフェースはデフォルト実装を持てるようにはなりましたが、主な役割は型やAPIの定義です。そのため、継承と言っても クラスの場合と異なり コードの再利用が目的となる場合は稀で、主にスーパーインタフェースに対するAPI(メソッド)の追加が目的になります。そのため インタフェースの継承の場合は「拡張」と呼ばれることもあります。(むしろ拡張の方が実態に近いと感じます。)

ポリモーフィズム

ポリモーフィズムには次に示す3種類があります。Javaで単にポリモーフィズムと言った場合、一般的にはサブタイピングのことを指します

  • アドホック多相(ad hoc polymorphism)
  • パラメータ多相(parametric polymorphism)
  • サブタイピング(subtyping/subtype polymorphism)

アドホック多相

ある同じ名前のメソッドが 異なる引数に対してそれぞれ異なる実装を持つことをアドホック多相といいます。Javaではメソッドのオーバーロードでアドホック多相を実現しています

パラメータ多相

特定の型を指定せずにコードを記述し、さまざまな型を適用できるようにすることをパラメータ多相といいます。さまざまな型を適用できるようにしておき 実際に使用する際に型を指定することにより、型安全性を担保することができます。Javaでは総称型でパラメータ多相を実現しています

サブタイピング

継承のところでも触れましたが、スーパークラスとして宣言された変数にサブクラスのインスタンスを格納することができます。これはリスコフの置換原則に基づくもので、SがTの派生型であれば T型のオブジェクトが使われている箇所は全て S型のオブジェクトで置換可能になります。この置換原則はクラスやインタフェースの継承だけに留まらず、インタフェースとそれを実装したクラスの間にも成り立ちます。派生元となる型をスーパータイプ、派生した型をサブタイプと呼びます。

Javaはダイナミックバインディングを採用しているため、同じようにスーパータイプのメソッドを呼び出しても、実行時のサブタイプのインスタンスにより異なった振る舞いをします。Javaで単にポリモーフィズムと言った場合は このような多態性のことを指し示します

スーパータイプを扱う抽象的なコードを書くことにより、実際に格納するインスタンスを別のサブタイプに置き換えることが容易になり クラス間の結合を緩やかにすることができますJavaではクラス・インタフェースの継承およびインタフェースの実装でサブタイピングを実現しています

ダイナミックバインディング

オブジェクトのメソッド呼び出しを コンパイル時の型ではなく実行時の型により決定する仕組みを ダイナミックバインディングといいます。Javaは基本的にダイナミックバインディングを行います。例外的にオーバーロードされているメソッドは コンパイル時の型でどれが呼び出されるかが決まります(スタティックバインディング)。

スーパータイプとして宣言された変数にサブタイプのインスタンスが格納されていれば、その変数に対するメソッド呼び出しはサブタイプのメソッドを呼び出します。

C++の場合は基本的にスタティックバインディングで、同じ状況ではスーパータイプのメソッドが呼び出されます。C++ではvirtualを指定することによって ダイナミックバインディングにすることも可能です。

オブジェクト指向プログラミング」(Wiki)
多相性とジェネリクス」(Qiita)
リスコフの置換原則」(Wiki)