2014年10月28日火曜日

- ポリモーフィズム - Java

三大機能、ラストは「ポリモーフィズム」です!

近頃は、無駄話が少なかったので、今日は少し前置きを長くします。

「カラカル」ってご存知でしょうか?
(ご存知の方は、すみません。知らないつもりで!)

聞いたことない!!って方に、まず画像を。















これを見て、なんだと思うか?
椅子や、オムライスには見えませんね。

動物に見えるはずです。
動物は動物でも、何に似てるかなー??と考えていきつくのは
おそらく、ネコ!(トラもかな)あたりですよね。

今日、学ぶ、ポリモーフィズムというのは
このカラカルをネコと捉えるテクニックです。

カラカルを人に、「四本足でー」、「胎生でー(卵を産まない)」、「夜行性でー」と長々と説明するよりも「ネコ科の動物だよ!」と一言伝えたら、おおよその姿形、習性はわかりますよね。

「厳密には、カラカル!だけど、ネコとして捉えよう!」というのがポリモーフィズムです。

その、ポリモーフィズムの利用に特殊な構文はない。(今まで学んできた、extendsやprivateのような)

今日は、自分だけの動物園を作るために、動物を沢山作ってみます。



package zoo;
// 継承の材料用のアニマル(動物)インターフェース
public interface Animal {

  void  eat();
  void sleep();

}

package zoo;
// アニマルインターフェースを継承したネコ科インターフェース
public interface Felidae extends Animal{
 void run();
}

package zoo;

public class Caracal implements Felidae{
 String name = "カラカル";
 public void eat(){
  System.out.println(this.name + "は、保護されているので、ご馳走をもらって食べている");
 }
 public void sleep (){
  System.out.println(this.name + "は、素敵な寝床で寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、すばしっこく走っている");
 }
}

package zoo;

public class Lion implements Felidae{

 String name = "ライオン";
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 c に Caracal インスタンス を格納
  Felidae c = new Caracal();

  // Felidae型の変数 l に Lion インスタンス を格納
  Felidae l = new Lion();

  c.eat();
  l.eat();

  c.sleep();
  l.sleep();

  c.run();
  l.run();

 }
}













クラス、インターフェースが沢山あってわかりづらいけれど、
ここで、重要なのは Mainクラス。








この一行が、何を表しているかが大事。

中身は ライオン !!! でも、 ネコ科 として捉える !!!

これは、継承の時に学んだ 「is - a の原則」と同じです。
(A is a B = A はBの一種である)

ライオン is a ネコ科 !になる。

なので、ここでも勿論、 is - a の原則から外れているものは代入できない。
たとえば 
Food f = new Lion();
Zoo z = new Tiger();
上の例は代入できない。

それでも、無理やり利用することは出来るのでは?
と思うかもしれないけど
抽象メソッドは全てオーバーライドしないと、エラーになってしまうので
is - a の原則を無視しているものは、中途半端なインターフェース、クラスになってしまう。
(作りたい抽象メソッドがあっても、子クラスで必要ないものは書くことができなくなる。)

しかし、この is - a の関係、人間ならば、感覚で理解出来るけれど
コンピュータはどうやって判断しているのか?

実は「extends」「implements」で判断している。
この二つは、継承関係を Java に教えるためのものでもあったのだ。


上のコードだと、通常の
Lion l = new Lion();
Felidae l = new Lion();
の違いがわからない。

少しだけコードを変えてみる。



package zoo;

public class Lion implements Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 c に Caracal インスタンス を格納
  Felidae c = new Caracal();

  // Felidae型の変数 l に Lion インスタンス を格納
  Felidae l = new Lion();

  l.eat();

  l.sleep();

  l.run();

  l.fight();
 }
}


Lionクラスに
新しいメソッド fight を追加して
Mainクラスで fight を実行させようとしています。

しかし、これはエラーになります。



















エラーメッセージをよくみると
メソッドfight()は型Felidae で未定義です
と書いてます。

コンピュータからみると、中身はLionでも、完全にFelidaeに見えているようです。

・呼び出し側は、同一視
・呼び出される側は、自分に決められた動きをする


ちょっとした実験。


package zoo;

public class Lion implements Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}

package zoo;

public class Tiger extends Lion{

 String name = "トラ";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}


package zoo;

public class Main {

 public static void main(String[] args) {


  // Felidae型の変数 l に Tiger インスタンス を格納
  Felidae t = new Tiger();

  // Lion型の変数 t に Tiger インスタンス を格納
  Lion l = new Tiger();

  t.fight();
  l.fight();
 }
}

Lionクラスの下にTigerクラスを作ってみた。
(こういう is - a じゃないものが、やってはダメな例です。すみません!)

他にもちょこちょこっと変更があったけれど、ここで重要な Lion,Tiger,Mainのクラスのコードだけ載せます。

特に注目したいのがMainクラスの

// Felidae型の変数 l に Tiger インスタンス を格納
Felidae t = new Tiger();

// Lion型の変数 t に Tiger インスタンス を格納
Lion l = new Tiger();

この部分。

t.fight();
l.fight();

この実行結果は








どんな型に代入しようが、中身がどちらも Tiger だと、実行結果も Tiger になるということ。

一つ上の、エラーコードの例とこの例から言えることは

型の箱にないものは呼び出せないし(エラーになって、実行すらできない)、他がどうあれ中身の箱に入っているメソッドが呼び出され実行される。


ここで、一つ疑問。
Felidae型(fightメソッドをもっていない)の箱に入ってしまった Lion が Fight()メソッドを呼び出すにはどうしたらよいのか。

解決方法は「変数 l の中身を、 Lion であると捉えなおす」という方法。
そのために利用するのが「キャスト演算子」である。


package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 l に Lion インスタンス を格納
    Felidae f = new Lion();

    f.eat();

    f.sleep();

    f.run();

    // キャスト演算子によって、強制的に f を Lion型の変数 l に Lion インスタンスを格納
    Lion l = (Lion) f;

    l.fight();

    }
 }





















このように、あいまいな型に入っている中身を、厳密な型に代入するキャストを
ダウンキャスト という。

本来、キャストはよほどの理由がない限る避けるもの。
なぜならば、失敗の危険が伴うから。

誤った代入を行った際は
「ClassCastException」
というエラーが出る。

これは、キャストにより強制代入の結果、「嘘の構図」になったため、強制停止せざるを得なくなった、という意味のエラーメッセージ。

誤った代入を未然に防ぐための演算子がある。
















この instanceof演算子は
if文と組み合わせて使ったりできる。


package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 l に Lion インスタンス を格納
    Felidae f = new Lion();

    f.eat();

    f.sleep();

    f.run();

    // もし、 f の中身が Lionならば
    if (f instanceof Lion){
    // Lion型の変数 l に Lion インスタンスを格納
    Lion l = (Lion) f;

    l.fight();
    }
  }
 }


これなら、もし中身が違う場合はダウンキャストが行われないので、エラーを防げる。



■ポリモーフィズムの利点■

ここまで学んできたけれど、今のところ、利点といえるものは見当たらない。
それどころか、親クラスにはなくて、子クラスにしかないメソッドが使えなくなり、不便に感じる。


しかし、上手に利用するとポリモーフィズムの利点がわかってくる。

次のコードをみてみる。


package zoo;
// 継承の材料用のアニマル(動物)インターフェース
public interface Animal {

  void  eat();

}


package zoo;
// アニマルインターフェースを継承したネコ科抽象クラス
public abstract class  Felidae implements Animal{

 private int hungryPoint;

 public int  getHungryPoint(){
  return this.hungryPoint;
 }
 public void setHungryPoint(int hungryPoint){
  this.hungryPoint = hungryPoint;
 }
 public void recover(int i) {

 }

}


package zoo;

public class Lion extends Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }

}


package zoo;

public class Caracal extends Felidae{
 String name = "カラカル";
 public void eat(){
  System.out.println(this.name + "は、保護されているので、ご馳走をもらって食べている");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }
}

package zoo;

public class Tiger extends Felidae{

 String name = "トラ";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  Lion l1 = new Lion();
  Caracal c1 = new Caracal();
  Tiger t1 = new Tiger();

  l1.setHungryPoint(10);
  c1.setHungryPoint(10);
  t1.setHungryPoint(10);

  l1.eat();
  c1.eat();
  t1.eat();

  l1.recover(l1.getHungryPoint());
  c1.recover(c1.getHungryPoint());
  t1.recover(t1.getHungryPoint());

  }
 }














Mainメソッドに、重複したコードが多いように感じる。
引数が変更になった際の修正も面倒。

こういう一括で引数をわたしたいときなどは、配列と組み合わせるとスッキリする。



package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の要素を三つを 配列変数名 f に代入
  Felidae [ ] f = new Felidae [3];

  // 要素の中に Lion,Caracal,Tiger を入れる
  f[0] = new Lion();
  f[1] = new Caracal();
  f[2] = new Tiger();

  // 拡張for文を使いループをまわす
  // f2 は Felidae型の任意の変数
  for (Felidae f2 : f){
   f2.eat();
  }
  for (Felidae f2 : f){
   f2.recover(10);
  }

 }
}


実行結果はさきほどと変わらない。
こういう使い方をするのが、どういう場面かは、わからないけれど
上の例を見ると、とても綺麗なコードになったと思う。
これで、回復度を10→7に変える際も、わずかな労力で変更可能。

また、同じ animalクラスから受け継がれてきた eat()メソッドを一括で呼び出しても
中身(Lion,Caracal,Tiger)に合った動作をそれぞれが実行してくれる。

これが、ポリモーフィズムのすごいところ!

2014年10月22日水曜日

- 継承 後編- Java

今回は「継承」の続き。
主に「抽象クラス」「インターフェース」について学ぶ。

内容は、そこまでは難しくない。
ただ、感覚が今までとは違う。

この、「抽象クラス」「インターフェース」は、「今」よりも「未来」に焦点をあてている。
今の自分のため!では、なく、第三者、未来の自分のために必要な道具である。

今はまだ、詳細が決まっていないメソッドがあるかもしれない。
そういうときに、役に立つのが、「継承」である。



■抽象クラス■

まず、継承の材料となるクラス(親クラス)を作る。
今までは、Catクラス、StrayCatクラス、BossStrayCatクラスなどを作ってきた。
Catクラスを使こともあったけれど、
今後は、「継承の材料となるクラス」は、newせずに使う(継承の材料のみの)クラスにしたい。

猫の話は、飽きてきただろうから、別のものを作る。

マリオブラザーズを作ることを考えてみます。(実際、ここでは作りません)
マリオがいて、敵が沢山いて。
敵が何をするかは、まだ考えていない。
けど、移動、攻撃、倒されるメソッドは必要だなー。

こういうとき、メソッドの中身を空にしておくという手もあるといえばある。
けれど、後で見返したときに、「実際に何もしない空のメソッド」なのか、「後から書き足す予定で空にしておいているメソッド」なのか、区別がつかない。
(コメントに書くという手もあるけれど、もし膨大な空のメソッドがあったら、大変ですし、他人や、未来の自分への負担が増える)

そこで、「抽象メソッド」というものを利用する。









この抽象メソッドの利用には、制約がある。

一つでも抽象メソッドを含むクラスは、「抽象クラス」というものにしなくてはいけない。
(抽象クラスになると、エラーが出る)












package blog;

public abstract  class Monster {

 // 以下、詳細未定メソッド
 public abstract void atack();

 public abstract void dying();

 public abstract void move ();
}


詳細未定メソッド atack,dying,move を定義した抽象クラス monster を書いた。

さらに、抽象クラスには制約がある。

抽象クラスは、newによるインスタンス化が禁止される!!
(newしようとすると、エラーが出る)

これが、上で書いた「抽象クラスはnewせずに使う、継承の材料のみのクラスにしたい」理由。
この制約により、何も知らない第三者が、継承の材料(と想定して作ったクラス)を誤ってnewすることもなくなる。

さらに、優れた機能がある。
まずは、この抽象クラスである monsterクラスを継承させた子クラスを作ってみる。
















上の画像は、 抽象クラスであるMonsterクラスを継承(extends)した Kuribouクラス を定義した際に現れたエラーメッセージです。

このエラーを消す方法は2つある

①Kuribouクラスの宣言の際に、 abstract をつける
②抽象メソッドを全てオーバーライドする

上に書いたようにabstract がついていたら、new出来ないため、
Kuribouクラスをインスタンス化させるためには、実質、全てオーバーライドする以外の方法はない。
なので、第三者、もしくは、未来の自分にオーバーライドを強制できる。



package blog;

public class Kuribou extends Monster {

 public void atack(){
  System.out.println("のんびり歩いて体をぶつけるよ!");
 }
 public void move(){
  System.out.println("左右にのんびり歩くよ!");
 }
 public void dying(){
  System.out.println("踏まれたらつぶれちゃうよ!");
 }
}
















オーバーライドによって、エラーメッセージは綺麗に消えました!


■インターフェース■

前回の継承で話したように、継承の階層が下がるにつれ、特化、あがるにつれ汎化する。
つまり、上流のクラスは、すべて抽象クラスになる。

特に抽象度が高いクラスは インターフェース にできる。
条件は

①すべてのメソッドが抽象メソッド
②基本的にフィールドを一つももたない。
(public static final がついたフィールドのみ許される。また、public static final は省略可能であり、フィールドを宣言すると自動的につく)











ここでも、ルールがあり、インターフェースとして宣言されたもののメソッドは、
自動的にpublic かつ abstract になる
(public abstract は省略可能。省略するのが一般的。)

さきほど、作った抽象クラス Monster は、全て抽象メソッド、フィールドも持たないという条件を満たしているので、インターフェースにしてみる。


package blog;

public interface Monster {

 // 以下、詳細未定メソッド
 void atack();

 void dying();

 void move ();
}


コードがスッキリしましたね。
しかし、ここで問題が出てきます。


























また、子クラスである Kuribouクラスにエラーメッセージが出てきました。
エラーメッセージに書いてあるように、
インターフェースを継承した子クラスを定義する際は、
extends ではなく implements を使う必要がある。
また、この行為は「実装する」と表現する。













package blog;

public class Kuribou implements Monster {

 public void atack(){
  System.out.println("のんびり歩いて体をぶつけるよ!");
 }
 public void move(){
  System.out.println("左右にのんびり歩くよ!");
 }
 public void dying(){
  System.out.println("踏まれたらつぶれちゃうよ!");
 }
}

extends の部分を implements に書き直して無事、エラーは消えた。


インターフェースって、そもそもなんだ??と思うけど、今は抽象クラスの親玉!!(あいまいなことしか言わない親玉)と思っておけば問題ない。
結局は、抽象クラスと継承の仕方は変わらず、Kuribouクラスでオーバーラードしなければnew出来ない。

つまり、
・このインターフェースを implements する複数の子クラスたちは、共通のメソッドを実装するように強制できる
・あるクラスがこのインターフェースを実装していれば、少なくとも、インターフェースが定めたメソッドは持っていることが保障される
(何度も言うが、オーバーライドしないと、new出来ないため!)

これだけみても、インターフェースと抽象クラスの違いがわからないと思う。
が、最大の強みがインターフェースには備わっている。

■インターフェースにのみ許される多重継承■

実は、Javaでは出来ないと思われていた(私が思っていただけですが!)多重継承が可能になる!

なぜ、多重継承がインターフェースにのみ許されているかというと……。
インターフェースをおさらいすると、抽象メソッドしかない。フィールドも持っていない。
つまり、内部実装がいっさい定められていない状態である。



















歩くモンスターインターフェース、飛ぶモンスターインターフェースを多重継承を利用して実装して
パタクリボー(空を飛ぶクリボーです)で、抽象メソッドである move() をオーバーライドする。
これは、なんの問題もない。

ダメな例(インターフェース以外から多重継承を試みる)は以下。


















歩くモンスタークラス、飛ぶモンスタークラス、どちらもメソッド move()があると、
実装の際に、どちらの動き方をするのかわからなくなってしまう。

そういう混乱を防ぐため、インターフェース以外からの多重継承は許されていない。
















package blog;

public  interface WalkMonster {

 void move();

}


package blog;

public  interface FlyingMonster {

 void move();

}


package blog;

public class PataKuribou implements FlyingMonster,WalkMonster {

 public void move (){
  System.out.println("飛んだり歩いたり!!");
 }

}


三つ以上のインターフェースを実装可能。

また、インターフェース同士の継承も可能。
その際は、implements ではなく、 extends と書く。
(クラス同士、インターフェース同士は extends)

クラスとインターフェースを両方使ったクラス定義も可能。













上手い例が思い浮かばなかったため、コードは書けない。
オーバーライドしつつ、インターフェースから継承した抽象メソッドを実装することができる。

2014年10月21日火曜日

- 継承 前編- Java

今日は、三大要素の二つ目「継承」について。


プログラムを書いていると似通ったクラスを作ることがある。

















この三つのクラスが必要になったとき、どうやって解決するか。

最も安易な解決方法は 「コピー&ペースト」


package blog2;

public class Cat {
 void eat (){
  System.out.println("食べる");
 }
 void sleep (){
  System.out.println("寝る");
 }
}

package blog2;

public class StrayCat {
 void eat (){
  System.out.println("食べる");
 }
 void sleep (){
  System.out.println("寝る");
 }
 void cadge(){
  System.out.println("ねだる");
 }
 void escape(){
  System.out.println("逃げる");
 }

}

package blog2;

public class BossStrayCat {

 void eat (){
  System.out.println("食べる");
 }
 void sleep (){
  System.out.println("寝る");
 }
 void cadge(){
  System.out.println("ねだる");
 }
 void escape(){
  System.out.println("逃げる");
 }
 void fight (){
  System.out.println("戦う");
 }

}

コピペを駆使して、作ってみた。
この程度なら、すぐに済む。
けれど、大きな問題が二つある。

①追加、修正が非常に大変
②把握や管理が難しくなる

コピペ戦法は、クラスもメソッドも多くない場合にしか、使えない。
実質、使えない。(大きな開発では、クラスがたくさんある)

最適な解決方法が 「継承」



















package blog2;

public class Cat {
 void eat (){
  System.out.println("食べる");
 }
 void sleep (){
  System.out.println("寝る");
 }
}


package blog2;

public class StrayCat extends Cat {
 void cadge(){
  System.out.println("ねだる");
 }
 void escape(){
  System.out.println("逃げる");
 }

}

package blog2;

public  class BossStrayCat extends StrayCat{

 void fight (){
  System.out.println("戦う");
 }

}


StrayCatクラス は Catクラス を元にして
BossStrayCatクラス は StrayCatクラス を元にした。
コードがスッキリしましたね。



















簡単なクラス図で表すとこうなる。
ボス猫が一番強そうだけど、あくまでも、親はCatクラス。

継承には、いくつか決まりがある。




























上のように、一つの親クラスから複数の子クラスへの継承は出来るが、
下のように一つの子クラスからの多重継承はできない。(Javaでは!)


継承そのものにも決まりがある。

■ is - a の原則■

A is a B (AはBの一種である)
という、英文に基づいた継承でなければならない。

正しい継承
子クラス is a 親クラス (子クラスは親クラスの一種である)

間違った継承
is - a の関係がない
例 マラソンクラス サッカークラス


間違った継承がダメな理由

①将来、クラスを拡張していった場合に現実世界との矛盾が生じる
  (オブジェクト指向の原則から外れる)
②三大要素の一つである「多態性」を利用出来なくなる

便利かどうかは関係ない!!

マラソンクラスとサッカークラスでは、同じ「走る」動作があっても、走り方は全然違う。
マラソンは「走る」競技だけど、サッカーは、「点を取る」競技。

このルールを守って正しい継承を利用すると
子クラスになるほど「特殊で具体的なもの」に特化する
親クラスになるほど「一般的で抽象的、曖昧なもの」に汎化する
(クラス図の継承関係を表す矢印は「クラスが汎化していく方向」を表す)

継承の利用によって出来ること
・コードの重複記述を減らす
・2つのクラスに、特化、汎化関係があることを示す



さて、ここで問題。
Catのeat と StrayCatのeat
同じ、食べる行為でも、違う文を表示したい場合。
野良猫なら、お腹をすかせているから、貪るように食べるかもしれませんね。

■インスタンスの多重構造■

継承の際、内部で何が起きているのか。



























StrayCatクラスをインスタンス化すると、Catクラス部分を持ったStrayCatインスタンスが出来上がる。
このように、継承の際は、インスタンスが二重構造になる。
(親、子、孫の場合は三重構造)




■オーバーライド■

親クラスを継承して、子クラスを宣言する際に、子クラス側のメンバを優先させることができる
これを オーバーライド という。



package blog;
package blog2;

public class StrayCat extends Cat {

 // 親クラスに定義してあるメソッドをオーバーライド
 void eat (){
  System.out.println("ガツガツ餌を食べた!!");
 }
 // 新規追加したメソッド
 void cadge(){
  System.out.println("ねだる");
 }
 // 新規追加したメソッド
 void escape(){
  System.out.println("逃げる");
 }

}

※注意※
宣言にfinalがついているクラスは継承ができない
宣言にfinalがついているメソッドは子クラスでオーバーライドできない


では、オーバーライドの際に、内部がどうなっているのかをみてみる。



























Cat でも StrayCat でも eat ( ) を持っている。
多重構造のインスタンスは極力、外側にある、子インスタンス部分のメソッドで対応する。
(が、実際には、内側のインスタンス部分のものから順に呼ばれている。後で説明。)

StrayCatの仕様が変更され、
StrayCatが空腹の場合は、「ガツガツ食べ」、そうではない場合は、Catのeat( ) メソッドと同様に普通の食べ方をさせてい場合。
親インスタンス部分へアクセスする方法がある。


package blog2;

import java.util.Random;

public class Cat {

 // フィールド hungryPoint を追加。0~4のランダムな整数が代入される
 int hungryPoint = new Random().nextInt(5);

 public void eat (){
  System.out.println("食べる");
 }
 public void sleep (){
  System.out.println("寝る");
 }
}

package blog2;

public class StrayCat extends Cat {

 // eat() メソッド
 public void eat(){
  System.out.println("ガツガツ食べた");

 // 親クラスの hungryPoint が3以下の場合
 if (super.hungryPoint <= 3){
  // 親クラスの eat()を呼び出す
  super.eat();
  }
 }

 // 新規追加したメソッド
 void cadge(){
  System.out.println("ねだる");
 }
 // 新規追加したメソッド
 void escape(){
  System.out.println("逃げる");
 }

}





この時に、使う super とは、「親インスタンス部」を表す予約語であり、
子インスタンス部分から、親インスタンス部分のメソッドやフィールドにアクセスする際に用いる。

注意事項があり、これは、子から親までのアクセスしかできない。
子から、祖父母(三重構造)へのアクセスはできない。

■継承とコンストラクタ■

new StrayCat ( ) した場合に、どういう順序でコンストラクタが呼ばれるか。

①まず、親インスタンス部が作られる
②外側に、子インスタンス部が作られる
③JVM(Java仮想マシン)により、自動的にコンストラクタが呼ばれる

ここで、次のコードをみてみる。


package blog3;

public class Cat {

 public Cat(){
  System.out.println("Cat出撃!!!");
 }
}


package blog3;

public class StrayCat extends Cat {

 public StrayCat(){
  System.out.println("StrayCat出撃!!!");
 }
}

package blog3;

public class Main {

 public static void main(String[] args) {

  StrayCat sc = new StrayCat();

 }
}


このコードの実行結果は以下。








Mainクラスで、StrayCat のみを インスタンス化しただけなのに
親クラス Cat の コンストラクタも呼び出されている。

実は、Javaでは、全てのコンストラクタは、その先頭で必ず親クラスのコンストラクタを呼び出さなければならないというルールがある。

親クラスのコンストラクタを呼び出すには

super (引数);

という一行を加えればよい。


package blog3;

public class StrayCat extends Cat {

 public StrayCat(){
  super();
  System.out.println("StrayCat出撃!!!");
 }
}

なので、本来は、このようにコンストラクタの一行目に
super();
を書かなければならない。
が、書いていない場合は自動的に super(); という行が挿入される。

自動で追加される super(); は、引数がない(0個)ということに注意しなくてはいけない。
引数が0個のコンストラクタが存在しない場合には、エラーが発生する。

その場合には、super(); に明示的に引数を渡さなければならない。

2014年10月13日月曜日

- カプセル化 - Java

今日から、三大要素について学んでいく。

三大要素を、非常にざっくりまとめた。
























詳細は、学んで、もしくは使って理解出来たらいいと思います。

今日、学習する「カプセル化」は、いわば、守りの要。
いったい、何から守るかというと、不具合です。

開発現場では、不具合への対応に多くの時間がさかれています。
もちろん、カプセル化だけでは、全ては防げないけれど、あるのとないのとでは、大きな違いがある。


カプセル化ってなに?ということろから。

カプセル化 とは フィールドへの読み書きや、メソッドの呼び出しを制限する機能。
キーワードは「制限」です。

現実世界で考えてみると……
・家に鍵をかけるようなもの
・赤ちゃんがいる家で、包丁が入っている扉を子どもの能力では開けられないようにするようなもの

もし、家に鍵をかけなかったら……赤ちゃんが自由に扉を開け閉め出来たら……。
思わぬ事故や、危険が発生する可能性がありますね。

カプセル化は、その事故や、危険を未然に防ぐためのものである。

このような「制限」を「アクセス制御 (access control)」という。

このアクセス制御は、メンバ(フィールド、メソッド)とクラスに設定が行える。

■メンバへのアクセス制御■

メンバ(フィールド、メソッド)に対するアクセス制御は四段階ある。


















private,public などを アクセス修飾詞 (access modifier) という。
フィールドや、メソッドを宣言する際に、先頭につける。

さっそく、 private を使ってみる。


package blog;

public class Tomomi {

 // アクセス修飾詞 private をつけたフィールドを宣言
 private int birthday;
 private String name  = "ともみ";

 void congratulate (){
  this.birthday = 1020;
  System.out.println(this.name +"の誕生日"+this.birthday + "には、お祝いをしよう!");
 }

}

package blog;

public class main {

 public static void main(String[] args){

 Tomomi  t = new Tomomi();

 t.congratulate();

 }
}









とくに、難しいことはない。

private をつけることにより、 フィールド birthday name は、他のクラスから書き換えることは出来なくなった。


実は、このアクセス制御は、だいたい使い方が決まってる

・フィールドは private
・メソッド(あと、クラス)は public

(ただし、テストをする際に、フィールドにアクセス出来ないと不都合が生じるため、なんでもprivateにすればいいわけではない。)

ここで、一つ問題が生じる。
これだと、フィールドを別のクラスからアクセスができない。

これだけでは、出来ないけれど
メソッドを経由してアクセスする方法がある。

■ getter と setter ■

getter メソッド と setter メソッド というものを作って、それを経由してフィールドにアクセスをする。
そうすると、フィールドの値に不具合が生じた場合、経由しているメソッドを調べれば、原因が特定できたりする。












前に、簡単に作っていて、我が家の近所の進化系をgetterメソッドを使って作ってみた。
setterメソッドを取り入れるのを失念していたため、今回はgetterメソッドのみで。
そのうち、setterメソッドを取り入れたものを作る。


package blog;

import java.util.Random;

public class StrayCat {

  String name = "野良猫ちゃん";

  // private である int型の変数 hungryPoint を宣言
  private int hungryPoint;

  // hungryPoint に 1~10 までの乱数を代入するメソッド
  void calcHungryPoint(){
   hungryPoint = new Random().nextInt(9)+1;
  }

  // private である int型の変数 hungryPoint を受け取るためのメソッド
  public int getHungryPoint(){
   return hungryPoint;

  }
  void cadge (){
   System.out.println(this.name + "は餌をねだった");
  }
  void eat (){
   System.out.println(this.name + "は、餌を食べた");
  }
  void escape (){
   System.out.println(this.name + "は、逃げ出した");
  }
}


package blog;

public class FeederLady {

  String name = "餌やりおばさん";

  void feed (){
   System.out.println(this.name + "は餌をあげた");
  }
  void escape (){
   System.out.println(this.name + "は逃げ出した");
  }
}


package blog;

import java.util.Random;

public class NextHouseLady {

  String name = "隣のおばちゃん";

  // private である int 型の変数 ragePoint を宣言
  private int ragePoint;

  // ragePoint に 1~10 までの乱数を代入するメソッド
  public void calcRagePoint(){
   ragePoint = new Random().nextInt(9)+1;
  }

  // private である int型の変数 ragePoint を受け取るgetterメソッド
  public int getRagePoint(){
   return this.ragePoint;
  }

  void rage (){
   System.out.println("おばちゃんは、激怒した!!");
  }
}

package blog;

public class main {

 public static void main(String[] args) {
  // TODO 自動生成されたメソッド・スタブ

  // 三つのクラスをインスタンス化
  StrayCat c = new StrayCat();
  FeederLady f = new FeederLady();
  NextHouseLady n = new NextHouseLady();

  // 先に乱数を生み出す calcHungryPointメソッドを呼び出す
  c.calcHungryPoint();
  System.out.println(c.name + "の空腹度は" + c.getHungryPoint() + "です");

  // getHungryPointが5以下の場合は終了
  if (c.getHungryPoint() < 5) {
   return;
  }

  // getHungryPointが5以下ではない場合に以下のメソッドを呼び出す
  c.cadge();
  f.feed();
  c.eat();

  // 先に乱数を生み出す calcRagePointメソッドを呼び出す
  n.calcRagePoint();
  System.out.println(n.name + "の不機嫌度は" + n.getRagePoint() + "です");

  // getRagePointが5以下の場合は終了
  if (n.getRagePoint() < 5) {
   return;
  }

  // getRagePointが5以下ではない場合は以下のメソッドを呼び出す
  n.rage();
  c.escape();
  f.escape();
 }

}
















これを使うことの利点は主に三つある

①ReadOnly,WriteOnlyのフィールドを実現できる
②フィールドの名前など、クラス内部設計を自由に変更できる
③フィールドへのアクセスを検査できる
   (たとえば、禁止ワードなどを、入力された時点ではじく設計に出来る。setterメソッドが重要)


■クラスのアクセス制御■

クラスに対するアクセス制御は二段階ある。











package privateクラスには特徴がある

①クラスの名前がソースファイルと一致しなくてもよい
②一つのソースファイルに複数宣言できる



これで、カプセル化についての学習は終わり。
使い方は簡単だけど、使いどころが大変難しい。
大規模開発の場合は、定められている方針に従ってカプセル化を使い分けると思います。
わたしが作るレベルでは、テストはあまりやらないので(やったほうがいいけど)基本的にフィールドをprivateにしてしまおうかなーと考えてます。

2014年10月8日水曜日

- 静的メンバ - Java

今回も、補足。

三大要素(カプセル化、継承、ポリモーフィズム)は難しいけれど、大切なので、その土台のために基本的な知識はしっかり理解しておく。
(丸暗記する必要はない)

今日は、比較的、簡単だから、さらっと書いていく。


静的メンバとは、静的フィールド、静的メソッドを指している。

この二つを表にした。






















一つずつみていく。


■静的フィールド(クラス変数)■

まず、静的フィールド(クラス変数)について。
どういうときに使うかというと、

同じクラスで生成されたインスタンスで、フィールドを共有したいとき!!

たとえば、現実世界で考えてみる。

「我が家の人間」クラスがある。
そこには 人間(続柄=夫、名前=さとし)、人間(続柄=妻、名前=ともみ)が生み出されている。
年齢、誕生日などのフィールドはそれぞれ違うけれど、
アイテムは共通である。(共有財産と呼ぶのかな)
掃除機、洗濯機、包丁、まな板などなど。

こういうときに、静的フィールドを使うと、「我が家の人間」クラスの持ち物に出来る。


package blog1008;

public class HomeMadeFamily {
 // 属性の定義
 String name;
 // 静的フィールドの定義
 static String item;

 void work (){
  this.name = "さとし";
  // 静的フィールド item に 文字列"圧力なべ" を代入
  this.item = "圧力なべ";
  System.out.println(this.name + "は、一生懸命働いて" + this.item + "を買った");
 }

 void cook (){
  this.name = "ともみ";

  // 静的フィールド item を呼び出せる
  System.out.println(this.name + "は、"+this.item + "を使って夕飯を作る!");
 }

}

package blog1008;

public class main {

 public static void main (String [] args){

 HomeMadeFamily f = new HomeMadeFamily();

 f.work();
 f.cook();

 }

}








無事に、「圧力なべ」を共有財産に出来ました。


静的フィールドの他の特徴もみてみる。


package blog1008;

public class HomeMadeFamily {
 // 属性の定義
 String name;
 // 静的フィールドの定義
 static String item = "圧力なべ";

 void work (){
  this.name = "さとし";
  // 静的フィールド item に 文字列"圧力なべ" を代入
  System.out.println(this.name + "は、一生懸命働いて" + this.item + "を買った");
 }

 void cook (){
  this.name = "ともみ";

  // 静的フィールド item を呼び出せる
  System.out.println(this.name + "は、"+this.item + "を使って夕飯を作る!");
 }

}

package blog1008;

public class main {

 public static void main (String [] args){

 // インスタンス化の一行をコメントアウトしてみる
 //HomeMadeFamily f = new HomeMadeFamily();

 System.out.println(HomeMadeFamily.item);

 }

}









このように、インスタンス化しなくても、静的フィールドは利用できますね。


■静的メソッド(クラスメソッド)■



package blog;

public class GlumlyLady {

 String name = "向かいのおばさん";
 // 静的フィールドの宣言
 static int glumlyPoint;

 // 静的メソッドの宣言
 static void randomGlumlyPoint(){
  // 静的フィールド glumlyPoint に 1~10 のランダムな整数を代入
  glumlyPoint = new java.util.Random().nextInt(10)+1;
 }
}

package blog;

public class main {

 public static void main(String [] args) {

  // new しなくても glumlyPoint を呼び出すことができる

  GlumlyLady.randomGlumlyPoint();
  System.out.println(GlumlyLady.glumlyPoint);

 }
}

実行結果は、整数一個だけなので、わざわざ載せませんが
1~10までの整数が表示されます。

ここで、注意点。
上の表に書いてある

static がついていない、フィールドや、メソッドが利用できない

とは、どういうことかというと。


package blog;

public class GlumlyLady {

 String name = "向かいのおばさん";
 // 静的フィールドの宣言
 static int glumlyPoint;

 // 静的メソッドの宣言
 static void randomGlumlyPoint(){
  // 静的フィールド glumlyPoint に 1~10 のランダムな整数を代入
  glumlyPoint = new java.util.Random().nextInt(10)+1;

  this.name = "ああああ";
 }
}

この、
this.name = "ああああ";

を書くとエラーになる。








このエラーメッセージが出てくる。

理由は、インスタンス化しなくても、呼び出せるメソッドなので、
一つもインスタンスがない状況では、 this.name の処理は出来ない。
なので、静的メソッドでは、静的フィールド、静的メソッドしか扱えない。


理屈を考えればわかるのだけど、初心者であるわたしが、はまってしまった罠でした。
クラスやメソッドを大量に書いていると、エラーの原因がわからなくなってきてしまうけれど、
エラーメッセージをよく読むと、書いてあることが多いから、
いくつかの失敗の原因を理解しておくのが大切ですね。
原因を理解していないときに、エラーメッセージを読んでも、よくわからなくなってしまいますし。

- コンストラクタ - Java

前回の「インスタンスとクラス」の補足。

補足といっても、内容は非常に重要なため、しっかり理解しておきたい。

コンストラクタとは…

new と同時に(インスタンス化)、実行されるメソッドのこと。

定義の決まりごとは
・メソッド名がクラス名と一致
・戻り値を記述しない
(コンストラクタが実行される際に渡して欲しい引数を、newするときに指定すればよい)



package blog;

public class Tomomi {

 int age;
 int mana;

 // インスタンス化(new)された直後に実行される処理を書いたメソッド
 Tomomi(){
  this.mana = 20;
  System.out.println("起きてすぐのマナの残量は"+ this.mana + "です");
 }

 // 引数を受け取る
 void study(int time){
  this.mana -= time;
  System.out.println("勉強して、マナが"+ this.mana + "になった" );
 }
 // 引数を受け取る
 void sleep (int time){
  this.mana += time;
  System.out.println("寝て、マナが"+ this.mana + "になった" );
 }
}

package blog;

public class main {

 public static void main (String []args){

  // インスタンス化
  Tomomi t = new Tomomi();

  // 引数を渡して、呼び出して実行
  t.study(9);
  t.sleep(7);
 }
}










Tomomi クラスの
 Tomomi( ) {

}
が、コンストラクトになっている。
これを見て、わかるように、コンストラクタは、mainクラスから、
newされるときに必ず最初に自動で実行される。
他のメソッドのように、直接呼び出すメソッドではない。

ただ、直接、情報を渡すこともできる。
定義の決まりごとの部分に、少し書いてある
「コンストラクタが実行される際に渡して欲しい引数を、newするときに指定すればよい」
これが、どういうことか実際にみてみると


package blog;

public class Tomomi {

 int age;
 int mana;

 // インスタンス化(new)された直後に実行される処理を書いたメソッド
 // newする際に、int型の引数を受け取る
 Tomomi(int mana){
  // 受け取った引数を フィールド変数 mana に代入
  this.mana = mana;
  System.out.println("起きてすぐのマナの残量は"+ this.mana + "です");
 }

 // 引数を受け取る
 void study(int time){
  this.mana -= time;
  System.out.println("勉強して、マナが"+ this.mana + "になった" );
 }
 // 引数を受け取る
 void sleep (int time){
  this.mana += time;
  System.out.println("寝て、マナが"+ this.mana + "になった" );
 }
}

package blog;

public class main {

 public static void main (String []args){

  // インスタンス化して、コンストラクタに情報を渡す
  Tomomi t = new Tomomi(20);

  // 引数を渡して、呼び出して実行
  t.study(9);
  t.sleep(7);
 }
}

実行結果はさきほどと、変わらない。

■コンストラクタのオーバーロード■

上の例を見返してみる。

Tomomiクラスは、int型の引数を1つ受け取るように定義されている。
(int型の引数が必ず必要!!)

しかし、同じ名前のクラスを、同時に定義できる。
以前、学んだ、オーバーロードを使う。



package blog;

public class Tomomi {

 int age;
 int mana;

 // インスタンス化(new)された直後に実行される処理を書いたメソッド
 // newする際に、int型の引数を受け取る
 Tomomi(int mana){
  // 受け取った引数を フィールド変数 mana に代入
  this.mana = mana;
  System.out.println("起きてすぐのマナの残量は"+ this.mana + "です");
 }

 // 引数を受け取らない
 Tomomi(){

  this.mana = 10;
  System.out.println("普段のマナの残量は" + this.mana + "です");
 }

 // 引数を受け取る
 void study(int time){
  this.mana -= time;
  System.out.println("勉強して、マナが"+ this.mana + "になった" );
 }
 // 引数を受け取る
 void sleep (int time){
  this.mana += time;
  System.out.println("寝て、マナが"+ this.mana + "になった" );
 }
}


package blog;

public class main {

 public static void main (String []args){

  // インスタンス化
  // 引数がないので、引数を受け取らないコンストラクタが呼び出される
  Tomomi t = new Tomomi();

  // 引数を渡して、呼び出して実行
  t.study(9);
  t.sleep(7);
 }
}










また、クラスは、インスタンス化するときに、何かしらのコンストラクタを実行する。
が、特例がある。
クラスに1つもコンストラクタが定義されていない場合は、
「引数なし、処理内容なし」のコンストラクタの定義が自動的に追加される。
(クラスとインスタンスのときに書いていたものが、これに該当する)


■コンストラクタから、別のコンストラクタを呼び出す■

重複する処理を使いたい場合など、別のコンストラクタから呼び出すことができる。


package blog;

public class Tomomi {

 int age;
 int mana;
 String name;

 // インスタンス化(new)された直後に実行される処理を書いたメソッド
 // newする際に、int型の引数を受け取る
 Tomomi(String name){
  // 受け取った引数を フィールド変数 mana に代入
  this.mana = mana;
  this.name = name;

  System.out.println("起きてすぐのマナの残量は"+ this.mana + "です");
  System.out.println(this.name + "1");
 }

 // 引数を受け取らない
 Tomomi(){
  // Tomomi(String name)のほうのコンストラクタを呼び出す
  this("ともみ");

  this.mana = 10;
  System.out.println("普段のマナの残量は" + this.mana + "です");
  System.out.println(this.name + "2");
 }
}

package blog;

public class main {

 public static void main (String []args){

  // インスタンス化
  // 引数がないので、引数を受け取らないコンストラクタが呼び出される
  Tomomi t = new Tomomi();

 }
}









わかりやすいように、コンストラクタの中の実行文に数字を入れた。

this("ともみ")
で、1つ目のコンストラクタは文字列「ともみ」を受け取って実行している。

注意点は
thisの後にクラス名ではなく、引数を書く!!


2014年10月3日金曜日

- インスタンスとクラス - Java

自分が神様となって、現実世界を真似た仮想世界に、互いに連携し合って動くオブジェクトを生み出す!!

オブジェクト指向の概要はなんとなく理解した。

我々が実際にやることは……













ここで注意点!!!

定義といっても、オブジェクトそのものを定義することは出来ない!!!










クラスを定義!!!

そこから、オブジェクトが生まれる!!!
































この例では、三つだからいいけれど、もし、クラス=オブジェクトだとしたら……
カップヌードル、綾波レイを大量に作ることになったらクラスが増えすぎてしまいますね!


このオブジェクトを インスタンス といい、
クラスから、インスタンスを生成することを インスタンス化 という。


■仮想世界を作ってみよう■

オブジェクト指向の時に、例に出した「うちの近所の争い」を作ってみます。
題して、「ヴァーチャル近所!!」

















きちんとしたクラス図(世界共通の設計図の1つ)で描くと












(名前がないとわかりづらいので、属性に name を追加しました)
ぱっと思いつくクラスはこの三つ。

・野良猫クラス
・餌やりおばちゃんクラス
・隣のおばちゃんクラス

が、しかし、これだけでは足りない!
指示(呼び出し)がないと、この三つのクラスは動かない。
指示を与えるクラスが必要。
創造主である自分の指示を書くためのクラスですね。
それが、 main クラスになる。

まずは、 mainクラス以外を作っていく。


























では、操作が少ない、「NextHouseLady」(隣のおばちゃん)クラスを書いていく。

①クラスの宣言

これは、今のところ特に難しいことはない。


public class NextHouseLady {

}


これだけ。わたしは、eclipseを使っているから、自分では書いてない。

②属性の宣言

作ったクラスブロックの中に、属性を書いていく。
隣のおばちゃんだと、属性は2つ。

・String type
・String name



package blog;

public class NextHouseLady {

 // 属性の定義
 String type;

 String name;
}


今までやってきた、変数宣言と変わらない。
(クラスブロック内に宣言された変数は フィールド という)
そして、今まで同様、宣言と代入は同時に行うことが出来る。



package blog;

public class NextHouseLady {

 // 属性の定義
 String type = "human";

 String name = "隣のおばちゃん";
}

また、「隣のおばちゃん」は、今後、何十年かたつと、「隣のおばあちゃん」になるかもしれないけれど、「人間」でなくなることは決してない。
一生、 String type = "human";
である!だから、書き換えられないようにしたい場合も変数と同様、
 final をつけておけばいい。
(定数フィールドという)


package blog;

public class NextHouseLady {

 // 属性の定義
 final String type = "human";

 String name = "隣のおばちゃん";
}

これで、属性の宣言は完了。

③操作の宣言

「隣のおばちゃん」の操作はたった一つ。
rage(怒る)だけ。
これを、メソッドにする。

package blog;

public class NextHouseLady {

 // 属性の定義
 final String type = "human";

 String name = "隣のおばちゃん";

 // 操作の定義を記述していく
 void rage() {
  System.out.println("激怒した");
 }
}

簡単に書くとこれだけ。
ただ、せっかく、名前があるから
「隣のおばちゃんは激怒した」
にしたい。

その場合は、


package blog;

public class NextHouseLady {

 // 属性の定義
 final String type = "human";

 String name = "隣のおばちゃん";

 // 操作の定義を記述していく
 void rage() {
  System.out.println(this.name + "は、激怒した");
 }
}


こうなる。
今までだと
System.out.println(name + "は、激怒した");
これでも、動くけれど、
ローカル変数、引数に同じ名前(name)があった場合に、予期せぬバグが出る原因になる。
だから、必ず、フィールドを扱うときは

this.フィールド名

にしなければいけない。
この「this」は、 自分自身のインスタンス という意味がなり
「.(ドット)」は英語では「of」、日本語では「~の」という意味になる。

これで、簡単なクラスの定義は完了。


■mainクラス■

クラスの定義が終わったので、mainクラスに指示を書いていく。
上にも書いたように、今はまだ、指示を出す mainクラス が書かれていないため、プログラムは動かない。

次は、 StrayCat(野良猫)クラスに対して、指示を与えていく。

StrayCat に新たに int型のフィールド hungry(空腹度を表す) を追加した。


package neighborhood;

public class StrayCat {

 // 属性の定義
 String type;
 final String name = "野良猫ちゃん";

 // 空腹度
 int hungry;

 // 操作の定義を記述していく

 void cadge() {
  System.out.println(this.name + "は、餌をねだった");
 }

 void eat() {
  System.out.println(this.name + "は、餌を食べた");
  hungry = hungry - 3;
  System.out.println(this.name + "の空腹度は" + this.hungry + "になった");
 }

 void escape() {
  System.out.println(this.name + "は、逃げていった");
 }
}


mainクラスでやりたいことは

① 野良猫を誕生させる
② 野良猫に、空腹度を設定する
③ 野良猫に、「ねだる」、「食べる」、「逃げる」指示を出す

上から順に書いていく。

まず、① 野良猫を誕生させる
というのが、どういうことかというと、
上で、クラスから生成されるのが、オブジェクトで、それをインスタンスという、と説明したように
野良猫のインスタンスを生成するということ。













クラスを定義することによって、インスタンスを生成できるようになる
さらに、定義したクラスの変数を利用できるようになる。
(この変数を、クラス型変数という)


package neighborhood;

public class main {

 public static void main(String[] args) {

  // 野良猫を生成(インスタンスを生成)
  // StrayCatクラスから インスタンスを生成して、変数 c に代入
  StrayCat c = new StrayCat();
 }

}

この場合、
StrayCatクラスから、インスタンスを生成して
生成したインスタンスをStrayCat型の変数 c を宣言、代入している。

(定義されたクラスの分だけ、利用できる型が増えていく!)


次に、② 野良猫に、空腹度を設定する
これは、StrayCatクラスが持っている属性(フィールド)を利用して、値を代入すること。












newなどはいらないので、簡単。


package neighborhood;

public class main {

 public static void main(String[] args) {

  // 野良猫を生成(インスタンスを生成)
  // StrayCatクラスから インスタンスを生成して、変数 c に代入
  StrayCat c = new StrayCat();

  // 野良猫フィールドの空腹度に初期値をセットする

  c.hungry = 10;

  System.out.println(c.name + "の空腹度は" + c.hungry + "です");
 }

}








最後、③ 野良猫に、「ねだる」、「食べる」、「逃げる」指示を出す
これは、インスタンスのメソッドの呼び出し。













package neighborhood;

public class main {

 public static void main(String[] args) {

  // 野良猫を生成(インスタンスを生成)
  // StrayCatクラスから インスタンスを生成して、変数 c に代入
  StrayCat c = new StrayCat();

  // 野良猫フィールドの空腹度に初期値をセットする

  c.hungry = 10;

  System.out.println(c.name + "の空腹度は" + c.hungry + "です");

  // インスタンスのメソッドを呼び出す

  c.cadge();
  c.eat();
  c.escape();
 }

}











これで3ステップは完了です!!!

ただ、これだと、物足りないので、時間をみて、改良します。