こんばんは!
バーチャル高専女子、七瀬真冬です。
今回のテーマは、みんな大好き「オブジェクト指向プログラミング」です♡
プログラミングを勉強している人なら一度くらいは耳にしたことがある言葉だと思います。
「オブジェクト指向」を理解することは、プログラミングを勉強する上で避けては通れない道。
非常に多くの開発現場で使われる、すごーく重要な概念なのです。
ところがこの「オブジェクト指向」、初学者には理解するのがとっても難しいと言われています。
この記事を読んでくれるほど勉強熱心なあなたは、「オブジェクト指向とは」でググった経験は1度や2度ではないはず。
で、実際ググって理解できたか?と言われたら、答えは恐らく「No」でしょう。
「オブジェクト指向とは何たるものか」を解説した記事は既にWeb上に溢れかえっていますが、ページ毎に書いてることがちょっとずつ違ったり、「そもそもオブジェクト指向なんて考え方は必要ない!」というぶっ飛んだ主張をする人まで現れたり。
もうカオス状態です。何を信用していいのかわからなくなるよね。
ぶっちゃけ、「オブジェクト指向」というものはかなりふわっとした概念です。
実際に現場で働いてるプロの人も、なんとなく雰囲気で理解しています。
だから言葉で教えるのが難しいし、人によって教え方が違うのです。
私の記事では、「オブジェクト指向ってのはこういうもんだ!深く考えるな!」っていうスタンスで書いていきます。
とりあえず手を動かして、最初にオブジェクト指向のお作法を覚えよう!というやり方です。
コードを書かずに、「オブジェクト指向のメリットは~」みたいな説明を読むだけでは、オブジェクト指向の考え方を習得するのは難しいです。
まずは実際に手を動かしてコードを書きながら、オブジェクト指向と仲良くなっていきましょう。
■ 本記事の対象読者
・オブジェクト指向について調べたことがあるけど、全く意味が分からなかったよ!
・C言語みたいな手続き型言語は書いたことあるけど、オブジェクト指向言語は未知の領域だよ!
といった人向けの記事になります。
■ 注意!!
私自身お仕事でオブジェクト指向言語を書いたことがないので、もしかしたらちょっと正確さに欠ける情報を書いているかもしれません。
「これは明らかにおかしいぞ!」って箇所があったら教えてください。
あと、スマホ版のレイアウトでは若干ソースコードの表示が崩れます。
スマホで見てる人は、PC版での閲覧を推奨します!
Webブラウザの設定に『PC版サイトを表示』みたいなやつがあるはずなので、それを選択してね。
■ 筆者の開発環境
・OS:Windows 10
・IDE:Eclipse Java 2019-03
オブジェクト指向言語にはたくさんの種類がありますが、今回は最もメジャーなオブジェクト指向言語といわれる「Java」を採用します。
まずはEclipseでJavaの開発ができる環境を整えましょう。
環境構築については私のブログでは説明しないので、各自ググってください。
開発環境の構築が終わって、Javaで"Hello World!"を出せるようになったら読み進めてもOKです。
スポンサーリンク
■ 目標は、「6つの概念」を理解すること。
皆さんがオブジェクト指向を学ぶ上での目標はズバリ、以下の6つの概念を理解することです。
・クラス
・インスタンス
・コンストラクタ
・カプセル化
・継承
・ポリモーフィズム
聞き慣れない言葉がいっぱいだね。
丁寧に1つずつ解説していくので、安心してください。
これは私個人の意見ですが、「オブジェクト指向を理解すること」は「これら6つの概念を理解し、使いこなすこと」だと考えています。
この6つの概念を全て理解できたら、「俺はオブジェクト指向完全に理解したぞ!」って言ってもいいのかも(?)しれません…。
今回は、この6点のうち
・クラス
・インスタンス
・コンストラクタ
の3点について説明します!!
カプセル化、継承、ポリモーフィズムはちょっぴり難しい概念なので、第2回、第3回、第4回にそれぞれ1つずつ解説する予定だよ。
■ まずは「クラス」から。
オブジェクト指向プログラミングにおいて、皆さんが作るものはクラスです。
皆さんはこれからクラスを作って、作って、作りまくるのです。
で、クラスっていうのはフィールドとメソッドを一纏めにしたカタマリのこと!
クラスの中には、フィールドとメソッドが入っているのです!
「まーた変な言葉が出てきた!!」とか言わないでください。
ちゃんと説明するから安心して。
フィールドってのは、要するに変数のこと。
正確には、クラス内に直接定義された変数のことをフィールドと呼びます。
バーチャル高専女子まふゆちゃんをモデルにしたMafuyuクラスで説明しよう。
Mafuyu.javaを作成し、以下のように記述してみてください。
(最初についてるpublicっていうのは次回で説明するから今は気にしないでね。)
public class Mafuyu { int age = 17; // 年齢! String costume = "セーラー服"; // コスチューム! String hairColor = "シルバー"; // 髪色! }
・int型のage(年齢)
・String型のcostume (コスチューム)
・String型のhairColor (髪色)
が定義されていることがわかりますね。
これらは3つは全て「フィールド」です。
これだけ。簡単ですね。次に行きましょう。
お次はメソッド。
メソッドっていうのは、要するに「動作」とか「処理」のことです。
文字を画面表示する!とか、数値と数値を足し合わせる!といった動作は全てメソッドの中に記述します。
C言語でいうと関数みたいなもんです。
さっきのMafuyuクラスに、データを画面表示するメソッドを追加してみました。
public class Mafuyu { int age = 17; String costume = "セーラー服"; String hairColor = "シルバー"; // 年齢を表示するメソッド! void printAge() { System.out.println("年齢は" + age + "歳だよ!"); } // コスチュームを表示するメソッド! void printCostume() { System.out.println("コスチュームは" + costume + "だよ!"); } // 髪色を表示するメソッド! void printHairColor() { System.out.println("髪色は" + hairColor + "だよ!"); } }
・年齢を表示するprintAge
・コスチュームを表示するprintCostume
・髪色を表示するprintHairColor
これらが「メソッド」です。簡単ですね。
はい、この時点で一つクラスが完成しました。
フィールド(変数)とメソッド(動作)を必要なぶんだけ定義したら、それでクラスの出来上がりです。
クラスの説明は以上。何も難しくないですね。
■ 「インスタンス」を作りましょう。
では、先程作ったMafuyuクラスを使って、年齢やコスチュームや髪色を表示してみるよ。
Mafuyu.javaと同一のパッケージ内に新しくMain.javaを作成し、mainメソッド内に処理を記述していきましょう。
public class Main { public static void main(String[] args) { Mafuyu mafuyu = new Mafuyu(); // ★ mafuyu.printAge(); mafuyu.printCostume(); mafuyu.printHairColor(); } }
実行した結果はこの通り。
しっかりと表示できていますね。
それではコードを解説。
コードの★の行を見てください。ここがいちばん重要なポイントです。
Mafuyu mafuyu = new Mafuyu();
これが何をしている処理かというと、Mafuyuクラスのインスタンス(実体)を作っているのです。
この書き方だと、小文字で書かれた「mafuyu」がインスタンスになります。
オブジェクト指向のプログラミングにおける大事なお作法の1つに、
「クラス」を実際に使える形にするには、「インスタンス」という実体を作らなければいけません!というものがあります。
ちなみに、インスタンスを生成することを「newする」という言い方をする場合も多いので、覚えておくといいでしょう。
さっき作ったMafuyuクラスを例にあげて説明するよ。
「クラス」っていうのは、フィールドとメソッドを記述しただけの設計書だと考えてください。
つまり、あなたがさっき作ったMafuyuクラスは「こういう値を持ってて、こういう動作をするよ!」っていう情報が書かれた設計書。ただの紙切れです。
そして、生成されたmafuyuが、まふゆちゃんの姿をもった実体(インスタンス)なのです!
で、その下の3行。
ここで、Mafuyuクラス内に定義されているメソッドを呼び出しています。
mafuyu.printAge();
mafuyu.printCostume();
mafuyu.printHairColor();
さっき生成したインスタンスmafuyuに、「.(ドット)」を付けることで、Mafuyuクラス内で定義したフィールドやメソッドにアクセスすることができます。
C言語でいうと、構造体のメンバにアクセスするような感じに似ていますね。
これまで学んだことをざっくりまとめると、
・クラスは、「フィールド(変数)」と「メソッド(動作)」をひとまとまりにしたもの。
・クラスはただの設計書。インスタンス(実体)を作ることで初めて使えるようになる。
・生成したインスタンスに「.(ドット)」を付けることで、クラス内で定義したフィールドやメソッドにアクセスできる。
重要なのは上記3点です。
クラスとインスタンスに関するイメージがつかめたら、次に進みましょう。
スポンサーリンク
■ 「インスタンス」は何個でも作れます。
インスタンスについて、もう少し詳しく解説するね。
実は、「インスタンス」はnewする度に何個でも新しく作ることが出来ます。
mainメソッドを以下のように変更してみてください。
public class Main { public static void main(String[] args) { Mafuyu mafuyu1 = new Mafuyu(); Mafuyu mafuyu2 = new Mafuyu(); System.out.println("---Mafuyu1の情報を表示します。---"); mafuyu1.printAge(); mafuyu1.printCostume(); mafuyu1.printHairColor(); System.out.println(""); // 空白行を入れる System.out.println("---Mafuyu2の情報を表示します。---"); mafuyu2.printAge(); mafuyu2.printCostume(); mafuyu2.printHairColor(); } }
mafuyu1、mafuyu2という2個のインスタンスを生成して、それぞれのデータを表示するコードですね。
実行結果は以下の通り。
このとき、mafuyu1とmafuyu2は姿形こそ全く一緒ですが、実際には全くの別人です。
以下の画像のようなイメージです。
mafuyu1とmafuyu2が別人であることを証明しましょう。
mainメソッドを以下のように変更してみてください。
public class Main { public static void main(String[] args) { Mafuyu mafuyu1 = new Mafuyu(); Mafuyu mafuyu2 = new Mafuyu(); // --- 追加部分ここから --- mafuyu2.age = 15; mafuyu2.costume = "メイド服"; mafuyu2.hairColor = "ピンク"; // --- 追加部分ここまで --- System.out.println("---Mafuyu1の情報を表示します。---"); mafuyu1.printAge(); mafuyu1.printCostume(); mafuyu1.printHairColor(); System.out.println(""); // 空白行を入れる System.out.println("---Mafuyu2の情報を表示します。---"); mafuyu2.printAge(); mafuyu2.printCostume(); mafuyu2.printHairColor(); } }
mafuyu2.age = 15;
mafuyu2.costume = "メイド服";
mafuyu2.hairColor = "ピンク";
といったように、mafuyu2が持っているフィールドの値を書き換えています。
図で表すとこうなります。
【補足】
オブジェクト指向プログラミングにおいて、フィールドの値を外部から直接書き換えるのは、一般的にはお行儀が悪いとされています。
本当はあんまりこういうことやっちゃダメです。これについては次回以降詳しく説明するよ。
実行結果はこちら。
mafuyu1とmafuyu2が持っている値は違うので、お互いに別人であることがわかりますね。
このように、「インスタンス」はnewすることによっていくつでも生成することが可能です。
mafuyu1、mafuyu2、mafuyu3 … mafuyu114514みたいに、いっぱい作ることだって出来ちゃいます。
よく使われるたとえ話で、
・「クラス」=「たい焼きの金型」
・「インスタンス」=「たい焼き」
と言われたりしますが、そういう考え方をしてもいいです。
今回の例でいうと、Mafuyuクラスが人形焼きの型で、newする度にかわいいまふゆちゃんカステラがポンポン生まれてくるような感じ。
型があればいくらでもインスタンスを生成できます。
とりあえず、インスタンスに関する説明はこんなもんでいいでしょう。
スポンサーリンク
■ 「コンストラクタ」を使いましょう。
今回の記事で説明する予定の3要素
・クラス
・インスタンス
・コンストラクタ
のうち、「クラス」と「インスタンス」に関する解説は終わりました。
これから、最後のひとつ「コンストラクタ」について説明するよ。
先程Mafuyuクラスからインスタンス「mafuyu」を生成するとき、
Mafuyu mafuyu = new Mafuyu();
という書き方をしました。
右側に赤色のカッコがありますよね。
先程は何も言わずにスルーしましたがこのカッコ、なんか気になりませんか?
頭のいい人は、「この右側のカッコは何だろう?」と思いながらこの記事を読んでいたはずです。
実はこのカッコが「コンストラクタ」と密接な関係を持っているのです。
ひとまずカッコの話は置いといて、まずは「コンストラクタとは何か」を説明しましょう。
コンストラクタとは、「インスタンスを生成したタイミング(newしたタイミング)で自動で実行されるメソッド」です。
一般的にコンストラクタは、インスタンスの初期化をする目的で使われます。
先程作ってもらったMafuyuクラスですが、コンストラクタが定義されていません。
なので、newされたタイミングで特に何かの処理が発生することはありませんでした。
Mafuyuクラスにコンストラクタを定義すれば、生成されるインスタンス(mafuyu1とかmafuyu2とか)を任意の値で初期化できます。
例をあげて説明するよ。
さっきインスタンスの説明をするときにmafuyu1とmafuyu2を使ったよね。
・mafuyu1は17歳、セーラー服、髪色はシルバー!
・mafuyu2は15歳、メイド服、髪色はピンク!
というふうにして、mafuyu1とmafuyu2は別人であることを説明しました。
mafuyu2は、生成された段階では「17歳、セーラー服、髪色シルバー」でしたが、直接フィールドを書き換えることによって「15歳、メイド服、髪色ピンク」に変更していました。
実はわざわざこんな風にフィールドを書き換えなくても、コンストラクタを上手に使えば、「15歳、メイド服、髪色ピンク」のmafuyu2を最初から作ることができます。
「12歳、スクール水着、髪色ブルー」のmafuyu3だって作れちゃいます。
ではでは、試しにMafuyuクラスにコンストラクタを定義してみましょう。
Mafuyuクラスを以下のように変更してください。
public class Mafuyu { // 初期値をセットするのをやめました。 int age; String costume; String hairColor; // これがコンストラクタだ! public Mafuyu(int age, String costume, String hairColor) { this.age = age; this.costume = costume; this.hairColor = hairColor; } void printAge() { System.out.println("年齢は" + age + "歳だよ!"); } void printCostume() { System.out.println("服装は" + costume + "だよ!"); } void printHairColor() { System.out.println("髪色は" + hairColor + "だよ!"); } }
コンストラクタは、
public クラス名(引数)
という形で定義します。
Mafuyuクラスでは、以下のようにコンストラクタが定義されています。
public Mafuyu(int age, String costume, String hairColor)
引数は好きな数だけ定義できます。
今回の場合、定義すべき引数は
・int型のage
・String型のcostume
・String型のhairColor
の3つです。
コンストラクタで行っている処理内容を見てみましょう。
// これがコンストラクタだ! public Mafuyu(int age, String costume, String hairColor) { this.age = age; this.costume = costume; this.hairColor = hairColor; }
thisという謎のワードが出てきましたね。
このthisは、クラス自身のことを表しています。
この場合、thisはMafuyuクラスのことを指しているのです。
this.ageは、Mafuyuクラスに定義されているフィールド「age」のこと。
this.costumeは、Mafuyuクラスに定義されているフィールド「costume」のこと。
this.hairColorは、Mafuyuクラスに定義されているフィールド「hairColor」のこと。
つまり、このコンストラクタ内では、Mafuyuクラス内のフィールド「age」「costume」「hairColor」にそれぞれ引数の値をセットしています。
引数として年齢、コスチューム、髪色を渡すことによって、あなた好みの年齢、コスチューム、髪色をもったまふゆちゃんのインスタンスを生成できちゃうのです!
でもこの「this」、なんか違和感がありますよね。
Mafuyuクラス内のデータを表示するメソッドでは、thisを付けなくても「age」「costume」「hairColor」だけでフィールドを参照して、画面表示する処理を行っていました。
以下を見てもらえば分かると思います。
void printAge() { System.out.println("年齢は" + age + "歳だよ!"); } void printCostume() { System.out.println("服装は" + costume + "だよ!"); } void printHairColor() { System.out.println("髪色は" + hairColor + "だよ!"); }
実は、本当は「this」なんて付けなくても、クラス内では「age」「costume」「hairColor」だけでフィールドを参照することができます。
しかし、このコンストラクタでは引数がフィールド名と全く同じですよね。
例えばage(年齢)の場合、フィールド名もageだし、引数名もageです。これでは区別できないよね。
そこで、thisを付けることによって「これは引数じゃなくて、Mafuyuクラス内で定義されたフィールドだよ!」ということを明示的にアピールしているのです。
ちなみに、printAge、printCostume、printHairColorで表示している値を
age → this.age
costume → this.costume
hairColor → this.hairColor
に変更してもちゃんと動きます。
長々と語ってしまいましたが、要するにコンストラクタ内で「年齢、コスチューム、髪色に好きな値をセットしている」のです。
ではこのコンストラクタを使ってみましょう。
ここでようやく、さっき話した
Mafuyu mafuyu = new Mafuyu();
のカッコの意味が分かります。
mainメソッドを以下のように変更してください。
public class Main { public static void main(String[] args) { Mafuyu mafuyu1 = new Mafuyu(17, "セーラー服", "シルバー"); Mafuyu mafuyu2 = new Mafuyu(15, "メイド服", "ピンク"); Mafuyu mafuyu3 = new Mafuyu(12, "スクール水着", "ブルー"); System.out.println("---Mafuyu1の情報を表示します。---"); mafuyu1.printAge(); mafuyu1.printCostume(); mafuyu1.printHairColor(); System.out.println(""); // 空白行を入れる System.out.println("---Mafuyu2の情報を表示します。---"); mafuyu2.printAge(); mafuyu2.printCostume(); mafuyu2.printHairColor(); System.out.println(""); // 空白行を入れる System.out.println("---Mafuyu3の情報を表示します。---"); mafuyu3.printAge(); mafuyu3.printCostume(); mafuyu3.printHairColor(); } }
mainメソッド内の最初の3行に注目。
Mafuyu mafuyu1 = new Mafuyu(17, "セーラー服", "シルバー");
Mafuyu mafuyu2 = new Mafuyu(15, "メイド服", "ピンク");
Mafuyu mafuyu3 = new Mafuyu(12, "スクール水着", "ブルー");
もうお分かりですね。
カッコの中に、コンストラクタの引数を与えるのです。
実行結果はこちら。
これで、好きな年齢、好きなコスチューム、好きな髪色のまふゆちゃんを好きなだけ量産できるようになりました!
コンストラクタ万歳!!
試しに、あなた好みのまふゆちゃんのインスタンスを生成して、データを表示してみてください。
えっちなコスチュームは着せたらダメだよ!!
スポンサーリンク
■ 「オブジェクト」という言葉について。
最後に、「オブジェクト指向プログラミング」の「オブジェクト」という言葉について軽く説明します。
この「オブジェクト」という言葉ですが、ふんわりとした曖昧な使われ方をする言葉です。
「object」は直訳すると「モノ」ですが、オブジェクト指向プログラミングの世界では「クラス」や「インスタンス」を指す意味で使われる場合が多いです。
文脈によって、「クラス」を指すのか「インスタンス」を指すのか変わってきます。
例えば、
「Mafuyuクラスをnewしてオブジェクトを生成する」
という文脈の場合、この「オブジェクト」は「インスタンス」の意味で使われてることが分かると思います。
それなら最初から「インスタンス」って言えよって話なんですけどね。
とにかく、「オブジェクト」って言葉はすごく曖昧な表現です。
なんとなく、雰囲気で、感覚的な理解でみんな使ってる言葉なのです。
ひとまず「オブジェクト」という単語を目撃したら、あまり深く考えずに「クラスかインスタンスのことを指してるんだな~」って考えとけばOKでしょう。
■ まとめ
・「クラス」は、「フィールド(変数)」と「メソッド(動作)」をまとめたものだよ!
・「インスタンス」は、「クラス」を実体化したものだよ!
・クラスを実際に使うためには、インスタンスを生成する必要があるよ!
・インスタンスは、newする度に何個でも作ることができるよ!
・「コンストラクタ」を使うと、インスタンスを任意の値で初期化することができるよ!
・「オブジェクト」という言葉は、「クラス」や「インスタンス」を指すときに使われる、ふわっとした意味の言葉だよ!
これらは、オブジェクト指向プログラミングにおける基礎中の基礎です。
しっかり頭に叩き込んでおきましょう。
次回は、オブジェクト指向プログラミングを行う上で非常に大切な「カプセル化」の概念について説明します。
お楽しみに!!
ではでは~・:*+.\( °ω° )/.:+