superって何?
前回の車の例を使い、今回はsuperで親クラス側のフィールドやメソッドを指定します。燃費の変更や自動補給を通して、親の処理を活用する方法を見ていきましょう。
親クラスのフィールドにアクセスするsuper
この記事は第23講「オーバーライドって何?」の続きです。前回は、子クラスで同名のnenpiを宣言しても、親のdrive()が参照するnenpiは変わらないことを確認しました。
今回はsuperを使って、親クラスで宣言したフィールドそのものの値を変更してみましょう。superは、子クラスのコードから親クラス側のメンバを指定するための書き方です。
EconomyCarクラス
まず、親であるCarクラスです。今回使うメソッドをまとめて載せておきます。各ファイルは同じパッケージに置いてください。
Car.java
class Car {
int gas = 60; // ガソリン残量(リットル)
int nenpi = 10; // 燃費(km/L)
public void drive(int gas) {
this.gas -= gas;
System.out.println(gas * this.nenpi + "km走りました");
}
public void putGas(int gas) {
this.gas += gas;
System.out.println("ガソリンを" + gas + "リットル補給しました");
}
public void checkGas() {
System.out.println("ガソリンは残り" + this.gas + "リットルです");
}
}Carクラスが持つフィールドはガソリン残量と燃費です。走る距離は「引数で渡したガソリン使用量 × nenpi」で決まります。
このCarクラスを継承して、EconomyCarクラスを書きます。
EconomyCar.java
class EconomyCar extends Car {
EconomyCar() {
super.nenpi = 20;
}
}随分、スッキリしています。書いてあるのはコンストラクタのみです。子クラスにはnenpiを宣言していません。
コンストラクタのsuper.nenpi = 20;で、Carで宣言したnenpiの値を変更しています。EconomyCarを作ると、そのオブジェクトが持つnenpiは20になります。
今度はdrive()が読むフィールドそのものを変更しているので、drive()をオーバーライドしなくても、20km/Lで走るようになります。
CarTest.java
public class CarTest {
public static void main(String[] args) {
EconomyCar eCar = new EconomyCar();
eCar.drive(20);
eCar.checkGas();
}
}400km走りました
ガソリンは残り40リットルです前回は同名のフィールドが二つありましたが、今回はnenpiは一つだけです。燃費のデータと走る処理をCar側にまとめ、子クラスでは燃費の値だけを設定する形にできました。
SuperEconomyCarクラス
同じように、Carクラスを継承して、30km/Lで走るSuperEconomyCarクラスを書いてみましょう。
SuperEconomyCar.java
class SuperEconomyCar extends Car {
SuperEconomyCar() {
super.nenpi = 30;
}
}これだけです。先ほどと同じようにコンストラクタでnenpiの値を変更しています。今度は30km/Lです。
こんな風に、親クラスで宣言したフィールドを使えば、drive()を子クラスで書き直さずに燃費を変えられます。
初期値の10は、このオブジェクトでは30に変更されます。ただし、Carクラスの定義や他の車の値まで変わるわけではありません。nenpiはインスタンスフィールドなので、new Car()で作った別の車の燃費は10のままです。
親クラスのメソッドを呼ぶsuper
もちろん、superで指定できるのはフィールドだけではありません。親クラス側のメソッドの実装を呼ぶ時にも使います。
少し無理やりな例ですが、「自動で燃料を補給する車」を作ってみましょう。オートドライブ機能を使うと、走って使った分だけガソリンを補給してくれる車です。どうやって補給するのかは、ここでは置いておきましょう。(笑)
AutoCar.java
class AutoCar extends Car {
public void autoDrive(int gas) {
super.drive(gas);
super.putGas(gas);
}
}autoDrive()の中で、Carのdrive()とputGas()を順番に呼んでいます。引数のgasを両方に渡しているので、使った分だけガソリンを補給することになりますね。
このAutoCarクラスをインスタンス化して走らせてみましょう。比較のため、普通のCarも続けて走らせます。
CarTest.java
public class CarTest {
public static void main(String[] args) {
Car car = new Car();
car.drive(20);
car.drive(30);
car.putGas(40);
car.checkGas();
System.out.println("");
AutoCar aCar = new AutoCar();
aCar.drive(20);
aCar.autoDrive(30);
aCar.putGas(40);
aCar.checkGas();
}
}200km走りました
300km走りました
ガソリンを40リットル補給しました
ガソリンは残り50リットルです
200km走りました
300km走りました
ガソリンを30リットル補給しました
ガソリンを40リットル補給しました
ガソリンは残り80リットルですAutoCarではautoDrive(30)の中で30リットルを使った後、その30リットルを補給しています。その分、普通のCarより残量が30リットル多くなりました。
なお、このAutoCarではdrive()とputGas()をオーバーライドしていないので、superを付けなくても同じ処理が呼ばれます。superによる指定が必要になるのは、子でオーバーライドした後も、親の実装を呼びたい時です。
オーバーライドしたメソッドから親の処理を使う
AutoCarクラスに特別なautoDrive()を書くのではなく、drive()をAutoCarクラスでオーバーライドする方針に変えてみましょう。AutoCarを次の内容に置き換えます。
AutoCar.java
class AutoCar extends Car {
@Override
public void drive(int gas) {
super.drive(gas);
super.putGas(gas);
}
}aCar.drive(30)を呼ぶと、このオーバーライドされたdrive()が実行されます。その中のsuper.drive(gas)で、親の「走る処理」をそのまま使い、super.putGas(gas)で、親の「補給処理」をそのまま使っているわけです。これでAutoCarは通常運転で普通にガソリンを補充しながら走るようになりました。永久機関を作ってしまった。。
aCarを普通に走らせてみましょう。
CarTest.java
public class CarTest {
public static void main(String[] args) {
AutoCar aCar = new AutoCar();
aCar.drive(30);
aCar.checkGas();
}
}300km走りました
ガソリンを30リットル補給しました
ガソリンは残り60リットルですオーバーライドとsuperを組み合わせれば、親の処理を利用しながら、子の役割に合った処理を追加できます。
次回は、継承したクラスのコンストラクタと、super()の意味を学びます。
質問・コメント
読み込み中この記事についての質問や、学習して気づいたことをお寄せください。
この記事のコメント
旧サイトに寄せられたコメントも当時の投稿内容のまま掲載しています。現在の記事では変更している内容や、旧環境についてのやり取りも含まれます。
コメントを読み込んでいます。