インスタンスとクラス
結論
・インスタンスとクラスは全く別のものであり、混同してはならない。
・仮想世界で活動するのは「インスタンス(オブジェクト)」である。
・インスタンスを生み出す為の金型が「クラス」である。
仮想世界の作り方
●オブジェクトを生み出す手順
オブジェクト指向プログラミングで開発されたプログラムは、動作時に「現実世界をマネたそれぞれのオブジェクトが、互いに連携して動く仮想世界」を形成する。
このように考えると、開発者の仕事は以下の2つである。
①各オブジェクトが負うべき責務を考え、「属性」「操作」の種類と内容を定義する。
②各オブジェクトを仮想世界に生み出し、動かす。
ここでのポイント!
オブジェクトの定義という言い方はしないので注意しよう。
Javaでは、仮想世界の中で動く「オブジェクトそのものを開発者が直接定義することは許されない」。
その代わりに開発者は、「オブジェクトが生み出される際に用いられる、オブジェクトの設計図」である「クラス」を定義できる。
開発者の仕事をまとめると…「①クラスを定義して、②そのクラスに基づいてオブジェクトを生成する」。
●クラスとオブジェクトが別である理由
何故、「オブジェクト同士が連携する仮想世界」と作るために、わざわざ「クラスを定義して、そのクラスからオブジェクトを生成する」という複雑な手順を踏まなければならないのか??
↓
↓
それは、「オブジェクトを大量に作る必要がある状況」で便利だからだ。
例)銀行の「すべての口座情報の複雑な統計を計算するプログラム」
この時、仮想世界に「口座」オブジェクトが1,000あるなら1,000個のオブジェクトを生み出す必要がある。
そして、その「1,000個の口座オブジェクトそれぞれに対して、『属性として、残高・名義人・開設日があって…』という定義を繰り返す必要がある」としたら、それを作る開発者の作業は果てしないものである。
そこで登場するのが「クラス」。『属性として、残高・名義人・開設日があり…』という定義をした「口座クラス」を1度作っておけば、この「クラスから500個でも、1,000個でも必要な数だけオブジェクトを生み出すことができる」のである。
別の例でも、振込を受け付ける「Uketsukeクラス」を1つ準備しておけば、複数の受付係を生み出して、「並行して振り込み依頼を受け付ける」プログラムを作ることも出来る。
言うなれば、クラスはプラモデル工場にある「金型」と考え、元となる金型を1個作っておいて、そこにプラスチックを流し込んで、同じプラモデルを大量生産するようなイメージ。
「クラスとオブジェクトは全く違うものである」ということ。プログラムの動作時に「仮想世界の中で活躍するのは『オブジェクトだけ』であって、その金型であるクラスが仮想世界で活動することは基本ない。
「受付係の金型」が挨拶をしたり振込を受け付けることはない。
●オブジェクトという言葉は曖昧である
実は、実際の開発現場における技術的な会話や文章の中で単に「オブジェクト」という表現が用いられる場合、
「金型か、その金型から生まれた実体化を厳密に区別しない(会話上どちらでもいい)」ことがある。
つまり、『「オブジェクト」という用語は、時々クラスのことを指して使われることもあるかなり曖昧なもの』
「金型ではなく、その型から生み出された仮想世界で活動する実体」を厳密に示したい場合は「インスタンス」という用語を用いる。
クラスからインスタンスを生成する行為を「インスタンス化」という。
・仮想世界で活動するのは「インスタンス(オブジェクト)」である。
・インスタンスを生み出す為の金型が「クラス」である。
●プログラムに登場する2種類のクラス(例.登場人物クラス、神様クラス)
上記例の"猫クラス"と"ねずみクラス"のような登場人物を示すクラス(以降、「登場人物クラス」と言う)のみでは、プログラムは動かない。
何故なら、猫クラスやねずみクラスは、「誰かから指示をもらうことで責任を果たすために動く」ので、この2匹だけを開発しインスタンス化しても、いわゆる「指示待ち状態」になってしまっている。
つまり、私たち開発者という「神様」が、この2匹の登場人物にどのように動くか指示をする必要がある。「神様クラス」が必要になるということ。この神様からの指示を「mainメソッド」に記述する!!
ただ単にmainメソッドを記述したいだけだとしても、メソッドはクラスの中に作るというJavaのルールを守る必要がある。よって、Mainなどの適当な名前でクラスを1つ作り、その中にmainメソッドを作ることになる。
このMainクラスだけは「現実世界の登場人物を模したものではない」ですし、インスタンス化して利用するものではない。あくまでも仮想世界の神様として、それぞれの登場人物を生み出し、それらに対して指示を出すことが役割である。
プログラムを作る際には、「登場人物クラス」と「神様クラス」の2種類を作る必要があることを意識してみよう!!
Javaプログラムの組成に必要なクラス
・mainメソッドを含む、1つの「神様クラス」
・現実世界の登場人物を模した、複数の「登場人物クラス」
クラスの定義方法
●登場人物クラスの作り方
クラスには「どのような属性や操作」を持っているか記述する。
上記の図のように、あるクラスの設計内容を上から「クラス名」「属性」「操作」の一覧として並べる書き方は、
「クラス図」という設計図のルールに準じたもの。
クラス図は世界共通の設計図として定義された「UML」で定められている図の1つ。この基本設計に基づき、実際にJavaでプログラムを書くと…以下のようになる。
//HeroクラスをJavaのコードで表したもの
public class Hero {
String name; //属性の定義
int hp; //属性の定義
public void attack() {/* … */} //操作の定義
public void run() {/* … */} //操作の定義
public void sleep() {/* … */} //操作の定義
public void sit(int sec) {/* … */} //操作の定義
}
●クラスの宣言方法
以下のように記述。ソースファイル名もクラスと同一にすること。
// ↑ ソースファイル名とクラス名も一緒。
//中身のないHeroクラスを作ってみたもの。
public class Hero {
}
●属性の宣言方法
〇〇クラスが持つ属性について、プログラムで使用する変数名と型を考える。
例)
【名前】 name(String型)
【HP】 hp(int型)
これらの変数を、Heroクラスのブロック内に宣言したのが以下である…
//Heroクラスに名前とHPを変数として宣言
public class Hero {
String name; // 名前の宣言 「フィールドを追加」
int hp; // HPの宣言 「フィールドを追加」
}
上記のように…
・クラスブロック内に宣言された変数を、Javaでは「フィールド」と言う。
・フィールド宣言:属性を宣言するにはクラスブロックの中に変数宣言を記述する。
これで、nameとhpという2つのフィールドの宣言が完了したわけである。
●「属性の初期値指定」と「定数フィールド」
★「属性の初期値指定」
「フィールド宣言」と同時に「初期値の設定(値の代入)」を行うことができる。
//
public class Matango {
int hp;
int level = 10; //お化けのレベルに初期値10を設定した
}
★「定数フィールド」
フィールド宣言の頭に「final」を付けると、『値を書き換えることの出来ない「定数フィールド」』になる。
なお、定数フィールドの名前は一目で分かるように、大文字で記述することが推奨されている。
フィールドを定数として宣言
final int LEVEL = 10;
※フィールドLEVELは10で固定。
public class Matango {
int hp;
final int LEVEL = 10;
}
●操作の宣言方法
「操作」を定義するには、以下の4つを考える必要がある。
・操作の名前
・操作するときに必要な情報の一覧
・操作の結果として指示元に返す情報
・処理内容
・ある登場人物の操作を定義するために「メソッド」を使う。
・「this.変数」とは…
メソッド内部のthis.nameやthis.hpの「this」とは特別に準備された変数で「自分自身のインスタンス」を意味している。また「.(ドット)」には日本語でいう「の」と同じ意味である。
つまり、「this.hp = 100」とは、「自分自身のインスタンスのhpフィールドに100を代入」という意味。
例)Heroクラスにsleepメソッドを宣言
Heroクラスに「操作」を記述していく。ただ今回の例は、クラス図の全ての操作を一度に作るのは大変なので、「眠る」操作だけにする。
・操作の名前:sleep
・操作するときに必要な情報の一覧:なし
・操作の結果として指示元に返す情報:なし
・処理内容:眠った後はHPが100に回復する
//「眠る」操作
public class Hero {
String name;
int hp;
public void sleep() { //ここから下を追加
this.hp = 100; //自分自身のhpフィールド
System.out.println(this.name + "は、眠って回復した!"); //this.nameは自分自身のnameフィールド
}
}
●フィールドとメソッドは「メンバ」と総称される。
●クラスとメンバの命名ルール
対象 | 品詞 | 大文字/小文字の用法 | 例 |
---|---|---|---|
クラス名 | 名詞 | 単語の頭が大文字 | Hero、MonsterInfo |
フィールド名 | 名詞 | 最初以外の単語の頭が大文字 | level、itemList |
メソッド名 | 動詞 | 最初以外の単語の頭が大文字 | attack、findWeakPoint |
●クラス定義のまとめ
クラスを定義するには、まず「属性」と「操作」の一覧をクラス図にまとめる。そして、Javaのコードに書き換えて「フィールド」と「メソッド」ととして記述。
//例
public class Hero {
String name;
int hp;
public void sleep() { //眠る(sleepメソッド)
this.hp = 100;
System.out.println(this.name + "は、眠って回復した!");
}
public void sit(int sec) { //座る(sitメソッド) //何秒座るか引数で受け取る
this.hp += sec; //座る秒数だけHPを増やす
System.out.println(this.name + "は、" + sec + "秒座った!");
System.out.println("HPが" + sec + "ポイント回復した");
}
public void slip() { //転ぶ(slipメソッド)
this.hp -= 5;
System.out.println(this.name + "は、転んだ!");
System.out.println("5のダメージ!");
}
public void run() { //逃げる(runメソッド)
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
}
クラス定義による効果
●クラス定義によって可能になる2つのこと
①クラスに基づいて、インスタンスを生成できるようになる。
②そのクラスから生まれたインスタンスを入れる変数の型が利用できるようになる。例えば、Heroクラスを定義するとHero型の変数が利用できるようになる。
このようにクラスを定義することで利用可能になる型のことを「クラス型」という。
●クラス型変数とは
Javaで扱う変数には必ず何らかの「型」を持っている。今まで利用してきた”整数を入れるint型”などは「Javaが標準で準備しており、いつでも使える型」であった。
これにプラス、例えばHeroクラスを定義することで「Heroクラスから仮想世界に生み出されたインスタンスを入れることが出来るHero型」が使えるようになる。
「クラスを定義すればJavaで利用可能な型の種類はどんどん増えていく」。
クラス型変数の定義方法
クラス名 変数名;
※intやStringと同じ。
//Hero型の変数を宣言
Hero h;
//このHero型の変数hには、まだ勇者インスタンスは入っていませんが、実際にはhに「仮想世界に生み出した勇者のインスタンス」を代入して利用していく。
●クラス型変数が必要な理由
何故Javaにはインスタンスを入れるための変数の型(クラス型)が必要で、クラスを定義することにより利用できるようになるのか???
→「仮想世界に複数存在しうる同名インタンスの中から、特定の1つのインスタンスをプログラムとして識別する為」
図のように、クラスCatから2つの猫インスタンスを生み出したものだが、「眠れ」という指示を私たちから見て右側の猫にしたい場合どのように記述したらいいのか…。
紙の場合は、「右の猫」「左の猫」と表現することが出来るが、「仮想世界の中では2匹の猫を識別する方法がなく、指示を送ろうにも相手を特定することが出来ない。
しかし、もし2匹の猫が、それぞれ変数h1,h2に入っていたとしたら問題は解決される。
インスタンスの利用方法
●「神様クラス」(=「Mainクラス」)の作り方
登場人物クラスのみではプログラムは動かない。登場人物に指示をだす「天の声」が必要。
「神様のクラス」であるMainクラス(mainメソッド)を作っていく、
//例
public class Main {
public static void main(String[] args) {
/* ここに勇者への指示を書いていく */
}
}
●インスタンスの生成方法
通常、〇〇クラスという金型から実体のあるインスタンスを生成するには次のようにする。
インスタンスの生成方法
クラス名 変数名 = new クラス名();
ここで実際にインスタンスを生成しているのは右辺の「new Hero()」という部分であり、「=」によって生成したインスタンスをHero型変数hに代入している。
なお今回1行で書かれているが、下記のように2行で書いても構わない。
「Hero h;」
「h = new Hero();」
//例
public class Main {
public static void main(String[] args) {
// 1.勇者を生成
Hero h = new Hero(); //Heroクラスからインスタンスを生成し、変数hに入れる
}
}
●インスタンスのフィールド利用「フィールドへの値の代入」
上記のような生み出されたばかりの勇者hには、名前もhpもまだない為、それぞれのフィールドに値を代入していこう!!
フィールドへの値の代入
変数名.フィールド名 = 値;
//例)勇者インスタンスを生成して初期値を代入
public class Main {
public static void main(String[] args) {
// (1.勇者を生成)
Hero h = new Hero();
// 2.フィールドに初期値をセット
h.name = "ミナト"; //変数hのnameに代入
h.hp = 100; //変数hのhpに代入
System.out.println("勇者" + h.name + "を生み出しました!"); //h.nameでは、変数hのnameを取り出す
}
}
●インスタンスのメソッド呼び出し
様々指示を(例)勇者に送るが、この「指示」は実際には「メソッドの呼び出し」という形で実現するのである。
//仮想世界に勇者を生み出すプログラム
public class Main {
public static void main(String[] args) {
// (1.勇者を生成)
Hero h = new Hero();
// (2.フィールドに初期値をセット)
h.name = "ミナト";
h.hp = 100;
System.out.println("勇者" + h.name + "を生み出しました!");
// 3.勇者のメソッドを呼び出してゆく
h.sit(5); //5秒座れ
h.slip(); //転べ
h.sit(25); //25秒座れ
h.run(); //逃げろ
}
}
↓
次のコードを実行
javac Main.java Hero.java
java Main
↓実行結果 ※Hero.javaを作っているのを前提に…
//実行結果
勇者ミナトを生み出しました!
ミナトは、5秒座った!
HPが5ポイント回復した
ミナトは、転んだ!
5のダメージ!
ミナトは、25秒座った!
HPが25ポイント回復した
ミナトは、逃げ出した!
GAMEOVER
最終HPは125でした
着目して欲しいのは、
「mainメソッドの内容が『まるで物語のシナリオ台本』みたいでわかりやすい」点である。
インスタンス利用のまとめ
・インスタンスの生成にはnewを使う。
・フィールドを利用する場合は「変数名.フィールド名」と記述。
・メソッドを呼び出す場合は「変数名.メソッド名()」と記述。
オブジェクト指向のクラスは現実世界とつながっている
「文法のみ(非オブジェクト指向)」と「オブジェクト指向」の違いとは…
オブジェクト指向の考え方に沿ったクラスは現実の事柄をコードに置き換えたものである。