抽象クラスとポリモーフィズム
抽象クラスを使用してポリモーフィズムを実現させてみましょう。スーパークラスの型でサブクラスのオブジェクトを扱えることを思い出してください。抽象クラスの型で、抽象クラスを実装したオブジェクトを格納することができます。ただしその場合、呼び出すことができるのは抽象クラスで定義しているメンバーだけです。
これからいろいろな乗り物を操作できるシミュレーターを作成してみましょう。乗り物クラスを継承したスポーツカークラス、タクシークラスを作成して利用してみましょう。ポリモーフィズムを確認するために、乗り物のテストをするテストドライバークラスを作成して、テストドライバーが乗り物を動かすようにします。
○ ファイル
次のようにファイルを作成してください。
ファイル名 | Taxi.java |
---|
○ プログラム
乗り物クラスを継承したタクシークラスを作成しましょう。
Taxi.java
- public class Taxi extends Vehicle{
- //フィールド
- private int maxSpeed; //最大スピード
- private int unchin; //売上金額
- private double distance; //走行距離
- private boolean aboard; //乗車状態
- //コンストラクタ
- public Taxi(double gas, int maxspeed){
- super(gas);
- //ガソリン量が容量を超えている場合は、最大容量をガソリン残量にセット
- if(this.gas > 70){
- this.gas = 70;
- }
- this.maxSpeed = maxspeed;
- }
- //メソッド
- //加速するメソッド(オーバーライド)
- public void speedUp(int sp){
- //消費するガソリンがない場合は加速しない
- if(!((this.gas - sp /20.0) <= 0)){
- //加速
- this.speed += sp;
- this.gas -= sp / 20.0;
- //スピードが最大スピードを超えていたら、最大スピードにセット
- if(this.speed > this.maxSpeed){
- this.speed = this.maxSpeed;
- }
- }
- }
- //乗車するメソッド
- public String rideToTaxi(int price, double distance){
- //乗車中かどうか
- if(this.aboard){
- return "乗車中です";
- }
- //走行中かどうか
- if(this.speed != 0){
- return "停車してください";
- }
- //運賃、距離を加算
- this.unchin += price;
- this.distance += distance;
- this.aboard = true;
- return null;
- }
- //降車するメソッド
- public String getOffTaxi(){
- //乗車中かどうか
- if(this.speed != 0){
- return "停車してください";
- }
- //乗車の状態を変更
- this.aboard = false;
- return null;
- }
- //getter
- public int getUnchin(){
- return this.unchin;
- }
- public double getDistance(){
- return this.distance;
- }
- }
○ クラス図
○ 解説
3行目では、最大スピードを格納するフィールドを定義しています。インスタンス生成時に最大スピードを初期化し、加速するときにこのフィールドを参照して加速するかを決定します。
4~6行目では、売上金額、走行距離、乗車状態のフィールドを定義しています。
9~16行目では、コンストラクターを定義しています。このコンストラクターはガソリン量を受けとって、スーパークラスのコンストラクターを呼び出し、ガソリン量をセットしています。そのあと、ガソリンタンク容量と比較して、セットしたガソリン量が多ければ最大容量にセットするという処理をします。
20~32行目では、speedUpメソッドをオーバーライドしています。抽象クラスを継承すると、抽象メソッドは強制的にオーバーライドしなければなりません。今回は加速するときに、消費するガソリン量がない場合は加速しない、最大スピードに達していた場合はそれよりスピードが出ないようにしています。 スポーツカークラスと違い、消費する燃料を抑えています。
35~51行目では、乗車する処理を定義しています。車が走っているか、既に乗車している場合には人は乗せられないようにしています。止まっているか、空車の場合は引数として受け取った料金、距離、乗車状況を、プロパティを使って設定しています。
54~63行目では、降車する処理を定義しています。車が走っている場合は人を降ろせないようにしています。止まっていれば降車処理をしますが、今回は乗車状態を変更しています。
○ ファイル
次のようにファイルを作成してください。
ファイル名 | TestDriver.java |
---|
○ プログラム
次に、乗り物を運転するテストドライバークラスを作成しましょう。次にようにクラスを作成してください。
TestDriver.java
- public class TestDriver{
- //フィールド
- private int accelerate; //加速度合(km)
- private Vehicle car; //車
- //コンストラクタ
- public TestDriver(Vehicle car, int sp){
- this.car = car;
- this.accelerate = sp;
- }
- //メソッド
- //加速するメソッド
- public void speedUp(){
- this.car.speedUp(this.accelerate);
- }
- //減速するメソッド
- public void speedDown(){
- this.car.speedDown(this.accelerate);
- }
- }
○ クラス図
○ 解説
3行目では、加速度合いを保存する変数を宣言しています。また、4行目では、テストドライバーが操作する乗り物オブジェクトを保持する変数を宣言しています。この変数はVehicle型なので、Vehcileを継承したクラスであれば保持できることになります。そのためテストドライバーは乗り物を継承した様々なタイプの乗り物を動かすことができます。今回作成したタクシークラスと、先回作成したスポーツカークラスはVehicleクラスを継承しているため、保持することができます。
7~10行目ではコンストラクターが定義されています。引数に乗り物、加速度合いを受け取り、それぞれのフィールドにセットします。テストドライバーがどの乗り物を運転するかがテストドライバーオブジェクトを作るときに決まることになります。
14~16行目では、加速処理が定義されています。ここのspeedUpメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。自分が乗っている(フィールドに保持している)乗り物のspeedUpメソッドを呼び出す記述が15行目にあります。フィールド変数にセットされているスピード分だけ加速します。
19~21行目では、減速処理が定義されています。ここのspeedDownメソッドは、乗り物クラス・スポーツカークラス・タクシークラスとは直接関係はありません。そのため、別のメソッド名で定義しても問題ありません。ここでの振る舞いは、前述したspeedUpメソッドと同じです。自分が乗っている(フィールドに保持している)乗り物を減速させます。
○ ファイル
次のようにファイルを作成してください。
ファイル名 | TestDriverTest.java |
---|
○ プログラム
では次にテストプログラムを作成して、実行してみましょう。
TestDriverTest.java
- import java.io.*;
- public class TestDriverTest{
- //車の状態を表示するメソッド
- public static void showMeter(Vehicle car){
- //各フィールドの内容を表示する
- System.out.println("スピード:" + car.getSpeed() + "km/h");
- System.out.println("ガソリン:" + car.getGas() + "L");
- }
- public static void main(String[] args) throws Exception{
- //キーボード入力の準備
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- //選択メニューを表示
- System.out.println("走らせる車の種類を選んでください。");
- System.out.print("1)スポーツカー 2)タクシー:");
- String inputdata = br.readLine();
- //選択した車オブジェクトを生成
- Vehicle car;
- TestDriver driver;
- if(inputdata.equals("1")){
- //車とテストドライバーを生成
- car = new SportsCar(60);
- driver = new TestDriver(car, 20);
- }else if(inputdata.equals("2")){
- //車とテストドライバーを生成
- car = new Taxi(50, 60);
- driver = new TestDriver(car, 5);
- }else{
- System.out.println("車を作りませんでした。");
- return;
- }
- //状態を表示
- showMeter(car);
- while(true){
- //操作を入力
- System.out.print("1)加速 2)減速 9)終了:");
- inputdata = br.readLine();
- //操作によって分岐
- switch(inputdata){
- case "1":
- //加速する
- driver.speedUp();
- showMeter(car);
- break;
- case "2":
- //減速する
- driver.speedDown();
- showMeter(car);
- break;
- case "9":
- //プログラムを終了する
- return;
- default:
- showMeter(car);
- break;
- }
- }
- }
- }
○ コンパイルと実行
C:¥work>javac TestDriverTest.java
C:¥work>java TestDriverTest
走らせる車の種類を選んでください。
1)スポーツカー 2)タクシー:1
スピード:0km/h
ガソリン:60.0L
1)加速 2)減速 9)終了:9
C:¥work>java TestDriverTest
走らせる車の種類を選んでください。
1)スポーツカー 2)タクシー:2
スピード:0km/h
ガソリン:50.0L
1)加速 2)減速 9)終了:9
C:¥work>
○ 解説
16~18 行目では、乗り物の選択肢を用意しました。ユーザーはスポーツカーかタクシーを選ぶことになります。23~34行目では、選択した乗り物と、テストドライバーを生成しています。乗り物に応じてテストドライバーに加速の仕方を指示(乗り物と加速度合い)しています。
48~65行目では、メニューを表示してテストドライバーに加速、減速などの指示を与えています。テストドライバーは自分にセットされている乗り物に対してspeedUpメソッドやspeedDownメソッドを呼び出して車を走らせることになります。その状態をshowMeterメソッドで表示しています。スポーツカーかタクシーのどちらのspeedUp・speedDownメソッドが呼び出されるかは、テストドライバーにどちらのオブジェクトがセットされているかによって決まります。
抽象クラスを使用してポリモーフィズムを実現させることができました。各オブジェクトを作り、テストドライバーにテスト実行時に乗り物を指定し、加速させたり減速させたりさせ、その乗り物の状態を確認してメーターに表示しています。TestDriverクラスで呼び出しているspeedUpメソッドやspeedDownメソッドは、どのオブジェクトがセットされるかによって振る舞いが変わっているのを確認できたと思います。これを多態性、多様性(ポリモーフィズム)と言います。
抽象クラスでは同じような属性を持ったオブジェクトを作ることができます。乗り物クラスであれば、乗り物の部類に属するものを定義することができます。次に、属性が全く異なるものに対しても同じ処理を指定する方法を見てみましょう。