puzzle
@puzzle (kokoko)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

スーパークラスとサブクラス

Q&A

Closed

 Javaのオブジェクトについて

継承をした際に、スーパークラスの型にサブクラスの型を代入しているのをよく見ます。私は現在Javaを学習しているのですが、いまいちその利点が理解できていません。。。
例えば
Animal animal = new Dog();
のように、AnimalクラスをDogクラスが継承してDogクラスのスーパークラスがAnimalクラスの場合などです。
利点があるからこのように書くのだとは思いますが、初学者の私は
Dog dog = new Dog();
でいいと思ってしまいます。
どのような利点があるのでしょうか、どなたかご教授よろしくお願いします。

0

2Answer

まずDogとAnimalどちらの型に入れる方が良いかについては、そのあと変数をどう使うかによって変わります。
そのあとの処理がAnimalであれば何でもいいなら、変数の型をAnimalにすることで『今はDogを使ってるけどAnimalなら他のでもいいよ』ということを読み手に伝えられるメリットがあります。

ただ、これについては個人的な意見ですが、
そういう場合はたいてい

void doSomething(Animal animal){
  // Animalが必要な何かしらの処理
}

Dog dog = new Dog();
doSomething(dog);

のようなAnimalを取る関数にDogのインスタンスを渡すような作りになると思うので、スーパークラス型の変数にサブクラス型のインスタンスを代入する場面はほぼ無いかなーとは思います。

1つの変数ではなくListで考えると、メリットがわかりやすいと思います。

Dog dog = new Dog();
Cat cat = new Cat();
Animal someAnimal = new Dog(); // CatやRabbitでもいい

// 動物園で管理している動物のリスト
List<Animal> managedAnimals = new ArrayList<>();
managedAnimals.add(dog);
managedAnimals.add(cat);
managedAnimals.add(someAnimal);

// ドッグランに出場する犬のリスト
List<Dog> runnerDogs = new ArrayList<>();
runnerDogs.add(dog);
// Dogでなければadd出来ない

『動物が必要』か『犬が必要』かは作りたいものによって変わります。
それを適切に表現できる型を使う、というのが大事な所だと思います。

1Like

Comments

  1. @puzzle

    Questioner

    ありがとうございます!まさにそのようなリスト(配列)を使っていたところです。
    Car cars[] = {
    new RedCar();
    new GreenCar();
    }; (見にくくなって申し訳ありません...)
    このような場面でも同じような考え方ができるということでよろしいですか?
    Carのリストが必要なら上記の書き方で、GreenCarのリストが必要ならまた違う書き方という解釈をしています。
  2. その認識で問題ないと思います。
    GreenCarしか認めないような場面ならGreenCarの配列が適切ですし、
    RedCarでもいいしGreenCarでもいいし他のCarでもいい場面ならCarの配列が適切、というかんじです。

    (あと補足ですが、配列とリストは似ていますが別物です。)
  3. @puzzle

    Questioner

    丁寧にご説明ありがとうございます!上記の問題は納得することができました。
    リストについて触れたことがなかったので、学習してみようと思います!

まず第一に、スーパークラスの型にサブクラスの型を入れているわけではなく
スーパークラスの型の変数にサブクラスのオブジェクトを入れています。

端的に利点だけを挙げます
Dogクラスのほかに、Cat、FishクラスなどAnimalクラスを継承したものをたくさん作った場合、オブジェクト生成する時にいちいち固有の型の変数に入れるのはとても面倒です。
そこで、作ったオブジェクトをすべてAnimal型とみなします
以下は例です

Animal.java
public class Animal{
	String name;
	int age;
	String gender;

	Animal(String name, int age, String gender){
		this.name = name;
		this.age = age;
		this.gender = gender;
	}

	public void info(){
		System.out.println("名前:" + name + "\n年齢:" + age + "\n性別:" + gender);
	}

	public void run(){
		System.out.println(name + "は走ります");
	}
}
Dog.java
public class Dog extends Animal{
	Dog(String name, int age, String gender){
		super(name, age, gender);
	}
}
Cat.java
public class Cat extends Animal{
	Cat(String name, int age, String gender){
		super(name, age, gender);
	}
}
Fish.java
public class Fish extends Animal{
	Fish(String name, int age, String gender){
		super(name, age, gender);
	}

	//魚は走れないのでオーバーライド、run()メソッドを固有の処理に変えます
	public void run(){
		System.out.println(name + "は泳ぎます");
	}
}
MainClass.java
public class MainClass{
	public static void main(String[] args){
		//Dog, Cat, Fishクラスのオブジェクトを作り、それらをAnimal型配列に格納
		Animal[] animals = {
							new Dog("ポチ",7,"オス"), 
							new Cat("タマ",9,"メス"), 
							new Fish("魚",2,"不明")
							};

		//全て型がAnimal型なのでForEach文で繰り返せる!
		for(Animal animal : animals){
			animal.info();
			animal.run();
			System.out.println();
		}
	}
}
名前:ポチ
年齢:7
性別:オス
ポチは走ります

名前:タマ
年齢:9
性別:メス
タマは走ります

名前:魚
年齢:2
性別:不明
魚は泳ぎます

あくまでこれは利点の一つです。
サブクラス達をスーパークラスの型の変数に格納すれば、このようにforEachでAnimal型とみなして取り扱うことが出来ます。

もしこうしない場合は、いちいちDog型の変数、Cat型の変数、Fish型の変数を用意してやる必要があり、とても面倒です。

難しい言葉を使うと、これらはポリモーフィズムという考え方につながります

1Like

Comments

  1. @puzzle

    Questioner

    ありがとうございます!納得できました。書いてくださったコードに最近学習したことが詰まっていて、とてもいい勉強になりました。
  2. 継承まわりの話は正直めちゃくちゃ覚えること多くて難しいので大変ですよね
    勉強頑張ってください。
  3. @puzzle

    Questioner

    ありがとうございます!
    精進します。。。

Your answer might help someone💌