Java 継承

Javaでの継承は、基本的には多重継承は禁止されているようです。

java - 何故Javaでは多重継承は許されていないのか - スタック・オーバーフロー

Javaの継承

正しい継承

f:id:ts0818:20170704200836p:plain

f:id:ts0818:20170704202348p:plain

間違った継承

f:id:ts0818:20170704200833p:plain

複数の親クラスを持つことは禁止されています。複数の親クラスを継承しようとすることを、多重継承 というようですが、Javaでは許可していないようです。

 

オーバーライド

親クラスのメンバ(フィールドやメソッド)を子クラスで上書きすることを、オーバーライドというようです。 

package chap11;

public class Hero {
  /**
   * フィールド変数
   */
	public String name;
	private int hp;

	/**
	 * コンストラクタ
	 * @param String name
	 * @param int hp
	 */
	public Hero(String name, int hp) {
		this.name = name;
		this.hp = hp;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getHp() {
		return hp;
	}

	public void setHp(int hp) {
		this.hp = hp;
	}

  /**
   * attackメソッド
   */
	public void attack(){
		System.out.println("大事な大事なアタックチャ~ンス!");
	}

}

package chap11;

public class SuperHero extends Hero {

	/**
	 * フィールド変数
	 */
	private int sp;
	private boolean flying = false;

	/**
   * コンストラクター
   * @param String name
   * @param int hp
   * @param int sp
   */
	public SuperHero(String name, int hp, int sp) {
		super(name, hp);
		this.sp = sp;
	}

	/**
	 * 空も飛べるはずメソッド
	 */
	public void fly() {
		this.flying = true;
		this.sp -= 5;
	}

	@Override
	public void attack() {
		super.attack();
		if(this.sp > 0 && this.flying) {
			super.attack();
		}
	}

}

実行クラス

package chap11;

public class Main {

	public static void main(String[] args) {

		// Hero
		Hero h = new Hero("ヒーロー", 100);
		System.out.println(h.getName() + "見参!");
		h.attack();

		// SuperHero
		SuperHero sh = new SuperHero("スーパーヒーロー", 100, 20);
		System.out.println(sh.getName() + "見参!");
		sh.fly();
		sh.attack();
	}

}

@Overrideというアノテーションが付与されたメソッドが、オーバーライドされています。

継承とコンストラクタ

継承とコンストラクタのイメージ図 

f:id:ts0818:20170704212408p:plain

上図のような継承関係の場合、Godクラスのコンストラクタを実行すると、GodクラスはSuperHeroクラスに、SuperHeroクラスはHeroクラスのコンストラクタが実行されるのを待つ感じになります。

最終的には、

Heroクラスのコンストラクタが実行される

SuperHeroクラスのコンストラクタが実行される

Godクラスのコンストラクタが実行される

という順番になるようです。

これは、super() というコンストラクタが自動的に挿入されるためのようです。

public class God extends SuperHero {
  // ↓ デフォルトコンストラクタも明示的に記述しない場合は隠れている
  public God() {
    // ↓ 自動的に先頭行に挿入されている(明示的に記述しない場合は隠れている)
    // super();
  }
}

このような感じで、親クラス以外にはコンストラクタ内にsuper() があると思われるため、孫は子の、子は親のコンストラクタが実行されてから自分のコンストラクタが実行されるということではないかと思われます(再帰っぽいイメージですかね?)

デフォルトコンストラクタは消えるんじゃった...

ここで、気を付けたいのが、コンストラクタを明示的に記述しない場合、JVMが気を利かしてデフォルトコンストラクタを用意してくれます。(ソースコード上では見えないけど)

ただし、自分で明示的にコンストラクタを作成した時点で、JVMで用意してくれていたデフォルトコンストラクタは消えてしまいます。

f:id:ts0818:20170704220950p:plain

つまり、引数ありコンストラクタなどを自分で作成した場合、自分で明示的にデフォルトコンストラクタを作成するか、継承先で呼ぶsuper() に引数を指定する必要があります。

f:id:ts0818:20170704222552p:plain

継承も、ややこしさ満点ですね。多重継承のあるC++Rubyは想像を絶しそうですね。