ページ 11

javaで質問があります

Posted: 2012年11月09日(金) 20:17
by mi_l
javaをコツコツと勉強しているのですが。。。。

コード:

import java.io.*;

class Car
{
	private int num;
	
	public Car()
	{
		System.out.println("車作成");
	}
	
	public Car(int n)
	{
		num = n;
		System.out.println("ナンバーは" + n +"にしました");
	}
	
	public void setCar(int n)
	{
		num = n;
		
	}
	
	public void show()
	{
		System.out.println("ナンバー" + num + "の車を作成しました");
	}
}

class RaceCar extends Car
{

private int course;

	public RaceCar()
	{
		System.out.println("レーシングカーの作成");
	}
	
	public void setRace(int c)
	{
		course = c;
		System.out.println("こーす" + course + "に設定しました");
	}
	
	public void show()
	{
		System.out.println("こーす" + course + "の車を作成しました");
	}
}

class kazuate
{
	public static void main(String[] args)
	{
		Car cars = new RaceCar();
		
		cars.se
	}
}
の最後のcars.seのところを本当はcars.setRaceにしたいのですがエラーが出てしまします。。。

なぜですか?

Carクラスを継承して拡張したのがRaceCarクラスというようにしたかったのですが、、

これをコンストラクタにするとうまくいきます・・・

メソッドではなぜいけないのでしょうか?

教えてください。よろしくお願いします。

Re: javaで質問があります

Posted: 2012年11月09日(金) 21:14
by みけCAT
cars変数がCar型なので、Carクラスにある機能しかつかえないと思います。
したがって、RaceCarクラス固有の機能であるsetRace関数は使えません。

Re: javaで質問があります

Posted: 2012年11月09日(金) 22:03
by asd
すでにみけCATさんが回答している通りです。

コード:

		Car cars = new RaceCar();
このように記述すると、carsがCarクラスの変数なので、生成されるRaceCarクラス(サブクラス)のオブジェクトに対して「Carクラス(スーパークラス)として振舞いなさい」と命じることになります。

当然setRaceメソッドはサブクラスでしか定義されていないので、ここでsetRaceメソッドを使えてしまうと、
Carクラスに定義されていないメソッドを使った=Carクラスとして振舞っていないことになってしまいます。
Carクラスに定義していない(スーパークラスから継承もされていない)メソッドが使えてしまったら
混乱しちゃいますよね。

mi_l さんが書きました: Carクラスを継承して拡張したのがRaceCarクラスというようにしたかったのですが、、
今回の場合、Carクラス内にもsetRaceメソッドを定義し(内容は空でOKです)、RaceCarクラスでオーバーライドする形にすれば上記の呼び出しかたが可能になると思います。

Re: javaで質問があります

Posted: 2012年11月10日(土) 00:12
by mi_l
それでは、拡張といってもスーパークラスにあるコンストラクタやメソッドの中にサブクラスでオーバーライドして変数などを増やすという意味になるのでしょうか?

それとも、RaceCar cars = new RaceCar();にすればCarクラスを継承し、尚且つ、RaceCarクラスで新たにでたメソッドにも値が渡せるようになるのでしょうか?

Re: javaで質問があります

Posted: 2012年11月10日(土) 18:56
by asd
mi_l さんが書きました:それでは、拡張といってもスーパークラスにあるコンストラクタやメソッドの中にサブクラスでオーバーライドして変数などを増やすという意味になるのでしょうか?
今後の拡張や使い方により上記のようにしないとまずい場合もあります(下でバスを追加した例を挙げてます)
レースカーにしかないものの場合はmi_lさんの最初のようにRaceCarクラスでのみ拡張すればいいです。
(たとえばレースカーだけニトロを積んでるとか(ぉ))

例として今は車に属する(Carクラス)、レースカー(RaceCarクラス)だけですが、たとえばバス(Busクラス)を新しく定義するとします。

コード:

class Car
{
    protected int num;//サブクラスから直接アクセスできるようprotectedに変更
    
    public Car()
    {
        System.out.println("車作成");
    }
    
    public Car(int n)
    {
        num = n;
        System.out.println("ナンバーは" + n +"にしました");
    }
    
    public void setCar(int n)
    {
        num = n;        
    }

    public int getCar(){ return this.num; }//ナンバーを参照できるように追加
    
    //オーバーロードしないと何もできない
    public void setRace(int c)
    {
        System.out.println("未実装です");
    }

    public void show()
    {
        System.out.println("ナンバー" + num + "の車を作成しました");
    }
}

class Bus extends Car
{
    public Bus()
    {
        System.out.println("バスの作成");
    }
    
    public void setRace(int c)
    {
        System.out.println("バスはレースには参加できません");
    }
    
    public void show()
    {
        System.out.println("ナンバー" + num + "のバスを作成しました");
    }
}
Carクラス自体にも少し手を入れていますが、これでバスクラスができました。
では、レースカーとバスでレース準備をさせてみましょう。

コード:

class kazuate
{
    public final static int MAX_CARS_NUM = 2;//最大台数
    public static void main(String[] args)
    {
        Car cars[] = new Car[kazuate.MAX_CARS_NUM];//Carクラスの配列を定義
        cars[0] = new RaceCar();//第1コース・・レースカー
        cars[1] = new Bus();//第2コース・・バス
        
        //各車のレース準備
        for(int i=0;i < cars.length;i++){
            cars[i].setRace(1);
        }
    }
}
上記のようにすると車の種類が増えたとしても、配列のサイズ拡張と新しい種類の車クラスを宣言するだけで
レース準備のところは手を入れる必要がありません。
CarクラスにsetRaceメソッドの定義がないと上記のような呼び出し方はできなくなってしまいます。

考え方として車ならばレース準備はできるはず、ただしその方法は車の種類によって様々だから(スーパークラスで一律で定義できないから)
各車で改めて定義してもらう(サブクラスでオーバーライドしてもらう)ということになります。
mi_l さんが書きました:それとも、RaceCar cars = new RaceCar();にすればCarクラスを継承し、尚且つ、RaceCarクラスで新たにでたメソッドにも値が渡せるようになるのでしょうか?
今回の件についていえば上記でも動くことは動きます。
ただし、バスやF1カー、その他別の種類の車を新しく追加した際に、setRaceメソッドがあるクラスと無いクラスで処理を別々にしないとならなくなります。
変数も種類ごとに作ることになるので100種類の車ができると管理が煩雑になります。

どうしてもCarクラスの変数を使って実現したいのであればキャストを使うことで一時的にRaceCarクラスとしてふるまわせることもできます。
が、他の種類の車が増えるとやはりだめになります(carsがBusクラスのオブジェクトを参照している場合にはキャスト部分でRaceCarクラスにキャストはできないとエラーになります)

コード:

    public static void main(String[] args)
    {
        Car cars = new RaceCar();
        //Car cars = new Bus(); //これだと実行時エラー
        ((RaceCar)cars).setRace(1);//RaceCarクラスにキャストして一時的にRaceCarとして振る舞わせる
    }
長文の割に説明下手ですみません(´・ω・`)

Re: javaで質問があります

Posted: 2012年11月10日(土) 23:45
by mi_l
返信ありがとうございました。

説明もわかりにくくなんてないですよ。

ですが、今回の話でまた疑問が出てきてしまったのですが・・

もしCarクラスと(戦車)Tankクラスを作るとします

もちろん両方ともナンバー、ガソリンはあります。

しかし、戦車にはミサイル、など他の様々な要素もあります。

この場合、この後レーシングカー、バスなど作りたかった場合、Carクラスにミサイルのメソッド、レーシングカーであればジェットエンジンなどのメソッドも用意しなければ継承してオーバーライドしたに勝手にメソッドを作ったことになってエラーになりますよね?

でも、ナンバー、ガソリン、その他(鍵など)はかぶる場所がある・・・・

この場合は継承しないで1からまたバスだったらBusクラスをつくり先に作ったCarクラスにあるメソッドをもう1個作らなければならない感じですか??

教えてください!

Re: javaで質問があります

Posted: 2012年11月11日(日) 01:11
by Dixq (管理人)
上のクラスが何をインターフェイスとしてメソッドを用意する必要があるかを考えれば、上に作るべきもの、下にだけ用意すればよいものが見えてくると思います。

> レーシングカーであればジェットエンジンなどのメソッドも用意しなければ継承してオーバーライドしたに勝手にメソッドを作ったことになってエラーになりますよね?

車がエンジンを持っている場合、「基本エンジン」クラスのインスタンスを持てばよいかと思います。
車クラスとは別のエンジンクラスを作って持たせるということです。

基本エンジンから
→ジェットエンジン
→ディーゼルエンジン

等を派生させ、Carクラスには「基本エンジン」のインスタンス(_engineとする)を持てばよいでしょう。
でも、実際に生成するのは、派生クラス側で、
トラックならコンストラクタでディーゼルエンジンを親クラスの持つ_engineに入れ、
レーシングカーならコンストラクタでジェットエンジンを親クラスの持つ_engineに入れればよいでしょう。

Re: javaで質問があります

Posted: 2012年11月11日(日) 14:24
by mi_l
なるほど!!

今回はただ車を作るってことだけしか考えてなかったので・・・

エンジンクラスとか作ればより多くの種類の車を作った時にも便利ですよね!!

ありがとうございます!!

Re: javaで質問があります

Posted: 2012年11月12日(月) 11:13
by asd
無事に解決したようでよかったです。
管理人さんに思い切り助けられてますが(^^;)

またわからないことが出てきたら遠慮なく聞いてくださいね。