型
Javaは型(type)を重視した言語仕様となっています。
これは様々なクラスのインスタンスを上手に扱う為に必要な機能です。
変数の型
変数の型(type)はデータの種類を表しました。
例えば本(Book)というクラスがあるならば本(Book)という型の変数を定義することができます。
また、雑誌(Magazine)というクラスがあるならば雑誌(Magazine)という型の変数を定義することができます。
Book book = new Book(); Magazine magazine = new Magazine();
ところで、雑誌は本の一種ですから、本を拡張したものと考えることができます。
これをオブジェクト指向的に考えるのであれば、雑誌は本のサブクラスであると言えるでしょう。
ここで雑誌のインスタンスを考えてみましょう。
雑誌のインスタンスは雑誌であることは間違いありませんが、本であるとも言えます。
しかし、本のインスタンスは雑誌であるとは限りません(雑誌であるかもしれません)。
例えば絵本という種類の本(サブクラス)かもしれません。
つまり、スーパークラス(基底クラス)からサブクラス(派生クラス)を見た場合にはクラスを拡張しているのですが、サブクラス(派生クラス)からスーパークラス(基底クラス)を見た場合にはクラスが抽象的になっていると言えます。
したがって、サブクラス(派生クラス)のインスタンスはスーパークラス(基底クラス)型の変数に代入できます。
Book book = new Magazine();
この時、実体はMagazineのインスタンスであることには変わりません。
メモリのイメージは次のようになります。
しかし、次のようにスーパークラス(基底クラス)のインスタンスをサブクラス(派生クラス)型の変数に代入する事はできません。
Magazine magazine = new Book();
型変換
変数の型に関するスーパークラスとサブクラスの関係は参照型変数同士であった場合も同じような変換が可能です。
Magazine magazine = new Magazine();
Book book = magazine;
このようにサブクラス(派生クラス)からスーパークラス(基底クラス)へは暗黙的に型の変換を行うことができます。
この時に生成されるインスタンスは1つのみで、どちらの変数も同じMagazine型のインスタンスを参照していることに注してください。
Objectクラス
Javaでは継承を導入することによりクラスは階層構造となっているとみなせます。
上位のクラス(スーパークラス)に複数の派生クラス(サブクラス)がぶらさがる階層構造とみなせる為、無数のクラスを整理・分類していく時に有効となります。
しかし、スーパークラスはなんらかのクラスのサブクラスである場合もあり、階層構造の最上位にあるクラスがあるはずです。
Javaでは全てのクラスの最上位のスーパークラスはObject型である、という約束になっています。
我々は次のようなコードでクラスを宣言しました。
public class Foo { }
このコードではextendsキーワードを使い継承をしていないように読めますが、実はこのコードは次のように書かれていても同じクラスが作成できます。
public class Foo extends Object { }
このextends Objectは暗黙的に補完されるので通常は記述しませんが、全てのクラスは例外なくObjectクラスのサブクラスになっています。
よって次のようなコードが成立します。
Foo foo = new Foo();
Object object = foo;
このように全てのクラスはObjectのサブクラスとして定義される事はオブジェクト指向言語の特徴の1つです。
キャスト
サブクラスからスーパークラスへの変換は暗黙的に行われましたが、その逆は原則として成功するとは限りません。
Object object = new Foo(); // コンパイルエラー Foo foo = object;
この場合は上の行を確認すればFoo型に変換できることがわかりますが、一般的には型を変換できない状態でしょう。
このような、型を強制的に変換したい場合に使うのがキャストと呼ばれる機能です。
上のコードは次のように記述することでコンパイルが通るでしょう。
Object object = new Foo();
Foo foo = (Foo) object;
このように強制的に変換したい型を( )で囲んで指定することにより、変数(厳密には参照先のインスタンス)の型を変更する事をキャストと呼びます。
尚、変換できない型であった場合にはキャストは失敗します。
instanceof
キャストに失敗した場合、プログラムはエラーとなり終了してしまう為、キャストが必要な場合は先に型変換可能かどうかを確認する必要があります。
このような場合、instanceof演算子を用いてインスタンスの型をチェックすることができます。
Object object = new Foo(); if(object instanceof Foo) { // 安全にキャストが行われる Foo foo = (Foo) object; }
instanceof演算子はboolean型を返す演算子で、演算子の前に変数(インスタンス)を記述し、後ろにチェックする型を記述します。
もし、変数(インスタンス)が型と一致するか、型のサブクラスであった場合にtrueとなります。
すなわち、instanceofでチェックした型にはキャスト可能かの判断ができるわけです。
// キャスト (変換する型) 変数 // 型チェック 変数 instanceof 型