オブジェクト指向に入門する上で最も大切 (かつ、分かりにくい) と思われる、クラスとインスタンスの違いというか、オブジェクトの取り扱いについて解説します。なお、言語は Java や C# や Swift などの一般的なオブジェクト指向言語を想定しています。
クラスとインスタンスの違い
よく、クラスは設計図で、インスタンスはそれを元に作った実体、と言われます。そう聞くとなんとなく分かった気にはなるけど、具体的にはどういうこと? と聞かれるとうまく説明できない人が多いんじゃないでしょうか。
クラス = 設計図
クラスはオブジェクトの設計図です。(ちなみに、オブジェクトという言葉はインスタンスと同じ意味で使われることが多いですが、このようにクラスを包括する観念的な意味で使われることもあります。)
ここで重要なのは、クラスはあくまでも紙に書かれた設計図のようなもので、実際に人を乗せたり荷物を積んだり走ったりすることはできません。そのためには実体であるインスタンスを生成する必要があります。
クラス 自動車 {
プロパティ 運転手
プロパティ 同乗者
プロパティ 荷物
メソッド 走る()
メソッド 止まる()
}
インスタンス = 実体
インスタンスはオブジェクトの実体です。(実体と言っても、コンピュータのメモリ上の存在に過ぎませんが…。)
インスタンスを生成するには new クラス名()
または、言語によっては単に クラス名()
と書きます。ここで重要なのは、インスタンスは生成するたびに違うものが新しく作られるということです。「違うもの」というのは、同じ設計図から作るので見た目は一緒だったりしますが、ものとしては別々という意味です。(この記事では、異なるインスタンスは色で区別します。)
変数 Aさんの車 = new 自動車()
変数 Bさんの車 = new 自動車()
変数 Cさんの車 = new 自動車()
オブジェクトの取り扱い
よくある間違い
プロパティにアクセスしたり、メソッドを呼び出すには、インスタンスが必要です。と言われて、その場で新しくインスタンスを生成してしまうことがあります。例えば、このコードを実行すると何が起きるでしょうか?
new 自動車().運転手 = Aさん
new 自動車().同乗者 = Bさん
new 自動車().走る()
まず、新しい自動車のインスタンスを生成して、その運転手をAさんにします。
次に、新しい自動車のインスタンスを生成して、その同乗者をBさんにします。
さらに、新しい自動車のインスタンスを生成して、それを走らせます。
まとめるとこんな感じですね。AさんとBさんは別々の車に乗ってるし、走るのはまた別の車 (しかも誰も乗ってない! 自動運転?) です。
オブジェクトの変数
本来やりたかったことは、自動車を一台だけ用意して、Aさんが運転して、Bさんが同乗して、走ることのはずです。それには、オブジェクトの変数を用意して、作ったインスタンスを記憶させて、それを使うようにします。
変数 Aさんの車 = new 自動車()
Aさんの車.運転手 = Aさん
Aさんの車.同乗者 = Bさん
Aさんの車.走る()
自動車を一台用意して、
Aさんが運転して、
Bさんが同乗して、
走る!
インスタンスの受け渡し
さらに、オブジェクトを引数に取る関数を考えます。
関数 出発(引数 車) {
車.運転手 = Cさん
車.荷物 = プレゼント
車.走る()
}
関数 到着(引数 車) {
車.止まる()
車.荷物 を受け取る
}
これらの関数を呼び出すたびに新しい自動車を生成したらどうなるでしょうか?
出発(new 自動車())
到着(new 自動車())
プレゼントを乗せて出発した車と、到着した車は異なるインスタンスなので、プレゼントは受け取れません。
ちゃんとプレゼントを受け取るには、自動車を一台だけ用意して、出発する車と到着する車を同じインスタンスにする必要があります。
変数 Cさんの車 = new 自動車()
出発(Cさんの車)
到着(Cさんの車)
まとめ
これまで見てきたように、オブジェクト指向プログラミングでは、オブジェクト (インスタンス) の同一性というのが重要になります。この記事で取り上げたのは単純な例ですが、実際のプログラムではもっと複雑なオブジェクトが出てきます。そのような場合にも、インスタンスがいつどこで生成されて、どうやって受け渡されているのかを考えると良いでしょう。
最後に、素敵なイラストを提供してくださったいらすとやさんに感謝します。