一番かんたんなJava入門

これからJavaを始めようという人の為の超入門サイトです。丁寧、簡単にこだわった解説なので初心者にぴったりです

【Java】 継承とコンストラクタ super( )の意味

time 2017/02/17

 継承時のメソッドや変数の挙動についてはだいたい分かってもらえたと思いますが、コンストラクタについてはちょっとややこしい事になっていますので、説明したいと思います。

 継承についてよく分かっていない人は先に継承って何?を読んで下さい。

 またコンストラクタについてよく分からない人は先にコンストラクタって何? this( )の意味を読んで下さい。

sponsored link

継承とコンストラクタ

 まず例としてHumanクラスを作ります。

Human.java

public class Human {
    Human(){
        System.out.println("人間誕生");
    }
}

 コンストラクタを書くことで、Humanクラスをインスタンス化する際には「人間誕生」と標準出力に出るようにしています。

 このHumanクラスを継承したProgrammerクラスを書きます。

Programmer.java

public class Programmer extends Human {
    Programmer(){
        System.out.println("プログラマー誕生");
    }
}

 こちらは、インスタンス化する際に「プログラマー誕生」と出力されます。

 さて、このProgrammerクラスをインスタンス化してみましょう。

Main.java

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
    }
}

 実行結果↓

 プログラマーが誕生する前に、人間が誕生しているのが分かると思います。

 つまり、Programmerクラスをインスタンス化する際には、まず継承元であるHumanクラスのコンストラクタが先に呼ばれて、その後でProgrammerクラスのコンストラクタが呼ばれています。

 このように、あるクラスをインスタンス化する際、内部では、その親クラスが先にインスタンス化され、それに覆い被さる形で子クラスがインスタンス化されるようになっています。

 もし継承が数世代に渡っている場合は、一番上の世代から順にインスタンス化されます。

super( )とは

 このように、あるクラスをインスタンス化する際には、何もしなくとも親クラスのコンストラクタが勝手に呼ばれるようになっているわけですが、勝手に呼ばせるのではなく、自分でコーディングすることで親クラスのコンストラクタを呼ぶことも出来ます。

 それがsuper( )です。

 super( )というのは、一世代上の親クラスのコンストラクタを指します。

 例えば、Programmerクラスの中でsuper( )と書けば、Humanクラスのコンストラクタを呼ぶことになります。

 ただし、super( )は、いつでもどこでも呼びたいタイミングで親クラスのコンストラクタを呼ぶ為のものではありません。そりゃそうです。そもそもコンストラクタというのはインスタンスを作る際に呼ばれるものであって、勝手に呼ぶものではありません。

 じゃあ、super( )はどこに書けばいいのかと言うと、コンストラクタの一行目です。限定です。それ以外の場所にsuper( )を書くとコンパイルエラーになります。

 Programmerクラスのコンストラクタの一行目にsuper( )と記述することで、Programmerクラスがインスタンス化されるタイミングでHumanクラスのコンストラクタを呼べるわけです。

Human.java

public class Human {
    Human(){
        System.out.println("人間誕生");
    }
}

Programmer.java

public class Programmer extends Human {
    Programmer(){
        super();
        System.out.println("プログラマー誕生");
    }
}

 Programmerクラスのコンストラクタの一行目にsuper( )と書くことで、Humanクラスのコンストラクタを呼んでいます。

 この状態でProgrammerクラスをインスタンス化すると、

Main.java

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
    }
}

実行結果↓

 こうなります。

 しかし、そうなんです。
 super( )は、書いても書かなくても一緒なんです。

 実は、super( )は、全てのクラスのコンストラクタ内に、見えないながらもひっそりと存在しているんです。書いていなくても既にあるんです。

 だからこそ、上記で説明したように、あるクラスをインスタンス化しようとしたら、勝手にその親クラスのインスタンスまで作られるんです。

 再掲↓

 なのでsuper( )は書いても書かなくても、動作結果は全く同じです。

 じゃあ、super( )なんて要らないじゃん。と言いたくなると思いますが、super( )が活躍するのは親クラスのコンストラクタに引数を渡したい時です。

親クラスのコンストラクタに引数を渡す

 では、引数を受け取るコンストラクタを作ってみましょう。

 Humanクラスのコンストラクタを書き換えて、Stringを引数として受け取るように改造します。

Human.java

public class Human {
    Human(String s){
        System.out.println("人間誕生" + s);
    }	
}

 これでHumanクラスはインスタンス化する際に引数に文字列を受け取り、それが標準出力に「人間誕生」に続いて出力されるようになりました。

 試しにHumanクラスをインスタンス化してみましょう。

Main.java

public class Main {
    public static void main(String[] args) {
        Human human = new Human("おぎゃあ");
    }
}

実行結果↓

 いい感じですね。

 しかしこの時点で、実はHumanクラスを継承しているProgrammerクラスにコンパイルエラーが出ています。

 これはなぜかというと、Humanクラスのコンストラクタを引数なしで呼んでいるからです。

 ん?ちょっとややこしいですね。落ち着いて考えましょう。

 Humanクラスのコンストラクタを書き換えた(引数を受け取れるようにした)ことで、Humanクラスをインスタンス化する際には必ずStringを引数として渡さなければならなくなりました。

 それを継承しているProgrammerクラスのコンストラクタ内には、先程説明したように、super( )が隠れています。super( )というのは見ての通り、引数は空っぽです。

 つまり、この状態だと、Programmerクラスのコンストラクタを呼ぶと、引数なしでHumanクラスのコンストラクタを呼んでしまうことになるわけです。

 なのでコンパイルエラーです。

 このコンパイルエラーを回避する方法は2つあります。

①Humanコンストラクタに常に引数を渡すようにする
②Humanクラスに引数無しで呼べるコンストラクタを用意しておく

 まず、①Humanコンストラクタに常に引数を渡すようにするからやってみましょう。

 Humanコンストラクタに常に引数を渡すには、どうしたらいいでしょう?

 答えは簡単。Programmerクラスのコンストラクタ内で、super( )に引数を渡します。

Programmer.java

 public class Programmer extends Human {
    Programmer(){
        super("おぎゃあ");
        System.out.println("プログラマー誕生");
    }
}

 これなら、Programmerクラスのコンストラクタが呼ばれた際には、漏れなく「おぎゃあ」を引数に渡してHumanインスタンスを作ることになります。
 つまり、引数無しでHumanコンストラクタが呼ばれることはないので、コンパイルエラーは回避できます。

 「おぎゃあ」で固定するんじゃなく、もっと柔軟に誕生の瞬間の声を届けたいなぁ。。という場合は、こんな風にすれば大丈夫です。

Programmer.java

 public class Programmer extends Human {
    Programmer(String s){
        super(s);
        System.out.println("プログラマー誕生");
    }
}

 Programmerクラスをインスタンス化する際には必ず引数に文字列を受け取り、その文字列をHumanクラスをインスタンス化する際に使っています。

 実際、インスタンスを作ってみましょう。

Main.java

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer("うぇーい");
    }
}

実行結果↓

 ただし、この場合、もちろんProgrammerクラスのコンストラクタも引数無しで呼ぶことは出来なくなります。

 つまり、以下だとコンパイルエラーです。

Main.java

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer(); //←引数なし
    }
}

 

 では、先程のコンパイルエラーを回避するもう一つの方法、②Humanクラスに引数無しで呼べるコンストラクタを用意しておくをやってみましょう。

 ①の方法でProgrammerクラスのsuper( )をごにょごにょしたことは一旦リセットして考えて下さい。

 こっちは何も難しいことじゃありません。Humanクラスのコンストラクタをオーバーロードするだけです。

Human.java

public class Human {
    Human(String s){
        System.out.println("人間誕生" + s);
    }
	
    Human(){
        this("");
    }
}

 もし引数無しでコンストラクタを呼ばれたら、空の文字列を引数に渡しています(まあ別に空じゃなくてデフォルトは「おぎゃあ」ってことにしてもいいけど一応空にしておこう・・ってホンマにどっちでもいいです)

 this( )は1つ目のコンストラクタのことなんですが、ここらがちょっと理解不足な人はぜひともコンストラクタって何? this( )の意味をよく読んでみてください。

 これなら、引数無しでHumanクラスのコンストラクタを呼ばれても全然問題ないので、上記のようにProgrammerクラスを触らずともコンパイルエラーは解決です。

 Programmerクラスは変更無しです。

Programmer.java

 public class Programmer extends Human {
    Programmer(){
        System.out.println("プログラマー誕生");
    }
}

 ただし、この方法の場合、Programmerクラスのコンストラクタは文字列を受け取れないので、インスタンス化する際にはもちろん文字列を渡すことはできません。

Main.java

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer("おぎゃあ"); //←コンパイルエラー
    }
}

 つまり、Humanクラスは常に文字列なしでインスタンス化されることになります。

 ①と②を組み合わせることで、Programmerクラスをインスタンス化する際に文字列を渡しても渡さなくてもどっちでも対応できる状態を作ることができます。

 こうです。↓

Human.java

public class Human {
	Human(String s){
		System.out.println("人間誕生" + s);
	}
	
	Human(){
		this("");
	}
}

Programmer.java

public class Programmer extends Human {
	Programmer(String s){
		super(s);
		System.out.println("プログラマー誕生");
	}
	
	Programmer(){
		super();
                System.out.println("プログラマー誕生");
	}
}

※Programmerクラスの2つ目のコンストラクタ内に引数なしのsuper( )を書いていますが、上で説明したようにこれはあってもなくても同じです。
※残念なことに「プログラマー誕生」を出力するコードがかぶってしまって不細工なコードになってしまっています。2つ目のコンストラクタ内はthis("");のみにしてしまう方法もありますが、それだと親クラスのパラメータのデフォルト値(空文字)が子クラスに依存することになり、非常に良ろしくないので、こんな形で勘弁してください。。

Main.java

public class Main {
	public static void main(String[] args) {
		Programmer programmer1 = new Programmer("うぇーい"); //引数あり
		Programmer programmer2 = new Programmer();          //引数なし
	}
}

実行結果↓

 super( )を使うことによって、Programmerクラスのコンストラクタに渡した文字列を、Humanクラスのコンストラクタへと渡せているのが分かると思います。

 このように、super( )は、親クラスをインスタンス化する際に引数が必要な場合に、子クラスのコンストラクタから親クラスのコンストラクタへとその値を渡す為に使います

sponsored link

Androidアプリを作ろう

down

コメントする



一番かんたんなJava入門

Androidアプリの作り方

忘備録

私の作ったAndroidアプリ

おすすめプログラミングスクール

無料で試せる今がチャンス!

管理人

Nobuo_CREATE

Nobuo_CREATE

WordPressテーマPrinciple、マテリアルを作ったり、Androidアプリを作ったり、Java入門サイトを作ったり、本を書いたりしています。どうぞよろしく。 [詳細]



sponsored link

オススメ書籍

[オススメpoint]

 この本は全く何も分からない初心者の方にお勧めです。プログラミングをするには覚えなければならない事が無茶苦茶いっぱいありますが、この本は教えてくれる順番、その構成が素晴らしいです。RPGのゲームを作るというストーリーにのっとってちょっとずつ難しいことを教えてもらえます。
 無機質で膨大なデータが載っているような本は読む気にならないという方は、こういうストーリー仕立ての本でチャレンジしてみてはいかがでしょうか?(注:RPGを作る為の本ではありません。)

[オススメpoint]

 ある程度、Javaを読み書きできるようになったら、オブジェクト指向について学ぶべきです。本書は、抽象的で分かったような分からんようなオブジェクト指向について、非常に分かりやすい例を出して説明してくれています。オブジェクト指向とは何なのか?という本質を掴むのにこれほど適した本はないと思います。オブジェクト志向の理念を理解できれば、より効率のいいコードをより楽に書けるようになるはずです。Java上級者を目指すなら必読の一冊!

只今、急拡大中

JavaからのRuby on Rails入門

JavaからのRuby on Rails入門

COBOLからのJAVA習得