2019/02/06
継承時のメソッドや変数の挙動についてはだいたい分かってもらえたと思いますが、コンストラクタについてはちょっとややこしい事になっていますので、説明したいと思います。
継承についてよく分かっていない人は先に継承って何?を読んで下さい。
またコンストラクタについてよく分からない人は先にコンストラクタって何? 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("おぎゃあ"); //←コンパイルエラー
}
}
つまり、Programmerクラスのコンストラクタを呼んだ際にインスタンス化されるHumanクラスは常に文字列なしでインスタンス化されることになります。
では、Programmerクラスのコンストラクタから、Humanクラスの誕生の瞬間の声をコントロールできるようにして、なおかつ、誕生の声を指定する必要のない(指定してもしなくても良い)状態を作れないものでしょうか?
こうしてみてはいかがでしょう。↓
Human.java
public class Human { Human(String s){ System.out.println("人間誕生" + s); } Human(){ this(""); } }
※この例の限りでは2つ目のコンストラクタは無くてもいいです。
Programmer.java
public class Programmer extends Human { Programmer(String s){ super(s); System.out.println("プログラマー誕生"); } Programmer(){ this(""); } }
※Programmerクラスの2つ目のコンストラクタ内のthis(“”)は、1つ目のコンストラクタを空文字を引数に渡して呼んでいます。
Main.java
public class Main { public static void main(String[] args) { Programmer programmer1 = new Programmer("うぇーい"); //引数あり Programmer programmer2 = new Programmer(); //引数なし } }
実行結果↓
super( )を使うことによって、Programmerクラスのコンストラクタに渡した文字列を、Humanクラスのコンストラクタへと渡せているのが分かると思います。
このように、super( )は、親クラスをインスタンス化する際に引数が必要な場合に、子クラスのコンストラクタから親クラスのコンストラクタへとその値を渡す為に使います。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
[…] 参考: 【Java】 継承とコンストラクタ super( )の意味 | 一番かんたんなJava入門 […]
by 週刊Railsウォッチ(20180608)特集「RubyKaigi 2018後の祭り」、`Enumerable#index_with`は優秀、コントローラから`@`を消し去るほか 2018/06/08 19:34
superの使い方分かりました。ありがとうございます!
by 匿名 2021/06/18 17:30