【21章】Javaを学ぼう

今回の記事では、前章で作成したプログラムをもう少し高度な継承を学び、さらに改良していきます！
私自身のアウトプットの場となりますので、よろしくお願いいたします！

念の為、前章まで進めてきたプログラムの内容を載せておきます。下図をご確認ください。

```java:Main.java
import java.util.Scanner;

class Main {
 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  Car car = new Car();
  car.setName("フェラーリ");                  
  car.setColor("赤");
  Bicycle bicycle = new Bicycle();
  bicycle.setName = ("ジオス");             
  bicycle.setColor = ("青");

  System.out.println("【車の情報】");         
  car.printData(); 
  System.out.println("-----------------");
  System.out.print("給油する量を入力してください：");
  int litre = scanner.nextInt();
  car.charge(litre);                       

  System.out.println("=================");
  System.out.println("【自転車の情報】");
  bicycle.printData();                     
 }
}
```

```java:Vehicle.java
class Vehicle {
 private String name;
 private String color;
 private int distance = 0;

 public String getName() {
  return this.name;
 }
 public String getColor() {
  return this.color;
 }
 public int getDistance() {                             
  return this.distance;
 }
 public void setName(String name) {                     
  this.name = name;
 }
 public void setColor(String color) {                  
  this.color = color;
 }
 public void printData() {
  System.out.println("名前：" + this.name);
  System.out.println("色：" + this.color);
  System.out.println("走行距離：" + this.distance + "km");
 }
}
```

```java:Car.java
class Car extends Vehicle {      
 private int fuel = 50;

 public int getFuel() {
  return this.fuel;
 }

 public void printData() {
  super.printData();
  System.out.println("ガソリン量：" + this.fuel + "L");
 }

 public void charge(int litre) {
  System.out.println(litre + "L給油します");
  if(litre <= 0) {
   System.out.println("給油できません");
  } if else(litre + this.fuel >= 100) {
   System.out.println("満タンまで給油します");
   this.fuel = 100;
  } else {
   this.fuel += litre;
  }
  System.out.println("ガソリン量：" + this.fuel + "L");
 }
}
```

```java:Bicycle.java
class Bicycle extends Vehicle {
 
}
```

確認ができたと思いますので、開始していきたいと思います。

##サブクラスのコンストラクタ
コンストラクタでnameとcolorの値をセットできるようにします。サブクラスでコンストラクタを定義するときには**コンストラクタの先頭でスーパークラスのコンストラクタを呼びださなければならない**という決まりがあります。スーパークラスのコンストラクタを呼び出すには、**super()**を用います。

```java:Main.java
class Main {
 public static void main(String[] args) {
  Car car = new Car();                   
 }
}
```

```java:Vehicle.java
class Vehicle {
 Vehicle() {
  System.out.println("スーパークラスのコンストラクタです");
 }
}
```

```java:Car.java
class Car extends Vehicle {      
 Car() {
  super(); //スーパーコンストラクタを呼び出している
  System.out.println("サブクラスのコンストラクタです");
 }
}
```

```java:コンソール
スーパークラスのコンストラクタです
サブクラスのコンストラクタです
```

それでは実際にスーパークラスに定義されたフィールドに、コンストラクタで値をセットできるようにしてあげましょう。

```java:Vehicle.java
class Vehicle {
 private String name;
 private String color;
 private int distance = 0;

 Vehicle(String name, String color) {
  this.name = name;
  this.color = color;

 public String getName() {
  return this.name;
 }
 public String getColor() {
  return this.color;
 }
 public int getDistance() {                             
  return this.distance;
 }
 public void setName(String name) {                     
  this.name = name;
 }
 public void setColor(String color) {                  
  this.color = color;
 }
 public void printData() {
  System.out.println("名前：" + this.name);
  System.out.println("色：" + this.color);
  System.out.println("走行距離：" + this.distance + "km");
 }
}
```

```java:Car.java
class Car extends Vehicle {      
 private int fuel = 50;

 Car(String name, String color) {
  super(name, color);
 }

 public int getFuel() {
  return this.fuel;
 }

 public void printData() {
  super.printData();
  System.out.println("ガソリン量：" + this.fuel + "L");
 }

 public void charge(int litre) {
  System.out.println(litre + "L給油します");
  if(litre <= 0) {
   System.out.println("給油できません");
  } if else(litre + this.fuel >= 100) {
   System.out.println("満タンまで給油します");
   this.fuel = 100;
  } else {
   this.fuel += litre;
  }
  System.out.println("ガソリン量：" + this.fuel + "L");
 }
}
```

```java:Bicycle.java
class Bicycle extends Vehicle {
 Bicycle(String name, String color) {
  super(name, color);
 }
}
```

まず上図のように、スーパークラスとサブクラスの双方でコンストラクタを定義しなければなりません。そして、サブクラスのコンストラクタ内でsuper()に引数を渡し、スーパークラスのコンストラクタを呼び出しています。

##runメソッドの追加
runメソッドはそれぞれのクラスで処理内容が異なるので、Vehicleクラスに定義して継承するのではなく、それぞれのサブクラスに独自のメソッドとして追加していきます。しかしながら、CarクラスやBicycleクラスはサブクラスといえど、Vehicleクラスのprivateフィールドには直接アクセスすることはできません。そのため、runメソッドが、Vehicleクラスのdistanceフィールドにアクセスしようとした時、privateフィールドになっているためエラーが起こってしまいます。
まずはこのエラーが起こらないよう、ここまで学習してきたゲッターやセッターとは別の方法で解消していきます。

##protected
privateの代わりに、**protected**を用いると、クラス内とサブクラスからのみアクセスを許すフィールドを作ることができます。よって、distanceフィールドをprotectedに変更すれば無事にrunメソッドが動作するようになります。

**・public: どこからでもアクセス可能**
**・protected: そのクラスと子クラス内からのみアクセス可能**
**・private: そのクラス内からのみアクセス可能**

```java:Vehicle.java
class Vehicle {
 private String name;
 private String color;
 protected int distance = 0;        //子クラス内からアクセス可能にするため

 Vehicle(String name, String color) {
  this.name = name;
  this.color = color;

 public String getName() {
  return this.name;
 }
 public String getColor() {
  return this.color;
 }
 public int getDistance() {                             
  return this.distance;
 }
 public void setName(String name) {                     
  this.name = name;
 }
 public void setColor(String color) {                  
  this.color = color;
 }
 public void printData() {
  System.out.println("名前：" + this.name);
  System.out.println("色：" + this.color);
  System.out.println("走行距離：" + this.distance + "km");
 }
}
```

```java:Car.java
class Car extends Vehicle {      
 private int fuel = 50;

 Car(String name, String color) {
  super(name, color);
 }

 public int getFuel() {
  return this.fuel;
 }

 public void printData() {
  super.printData();
  System.out.println("ガソリン量：" + this.fuel + "L");
 }

 //runメソッドの追加
 public void run(int distance) {
  System.out.println(distance + "km走ります");
  if (distance <= this.fuel) {
   this.distance += distance;
   this.fuel -= distance;
  } else {
   System.out.println("ガソリンが足りません");
  }
  System.out.println("走行距離：" + this.distance + "km");
  System.out.println("ガソリン量：" + this.fuel + "L");
 }

 public void charge(int litre) {
  System.out.println(litre + "L給油します");
  if(litre <= 0) {
   System.out.println("給油できません");
  } if else(litre + this.fuel >= 100) {
   System.out.println("満タンまで給油します");
   this.fuel = 100;
  } else {
   this.fuel += litre;
  }
  System.out.println("ガソリン量：" + this.fuel + "L");
 }
}
```

```java:Bicycle.java
class Bicycle extends Vehicle {
 Bicycle(String name, String color) {
  super(name, color);
 }
 //runメソッドを追加
 public void run(int distance) {
  System.out.println(distance + "km走ります");
  this.distance += distance;
  System.out.println("走行距離：" + this.distance + "km");
 }
}
```

```java:Main.java
import java.util.Scanner;

class Main {
 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  Car car = new Car("フェラーリ", "赤");
  Bicycle bicycle = new Bicycle("ジオス", "青");

  System.out.println("【車の情報】");         
  car.printData(); 
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int carDistance = scanner.nextInt();
  // runメソッドを呼び出して、引数にbicycleDistanceを渡している
  car.run(carDistance);
  System.out.println("-----------------");
  System.out.print("給油する量を入力してください：");
  int litre = scanner.nextInt();
  car.charge(litre);                       

  System.out.println("=================");
  System.out.println("【自転車の情報】");
  bicycle.printData(); 
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int bicycleDistance = scanner.nextInt();
  // runメソッドを呼び出して、引数にbicycleDistanceを渡している
  bicycle.run(bicycleDistance);                    
 }
}
```

#抽象メソッドと抽象クラス
車や自転車に限らず、乗り物はすべて「走行する」機能を持ちます。
よってプログラム上でも、Vehicleクラスを継承するすべてのクラスはrunメソッドを持っているべきです。よって本来であれば、runメソッドはVehicleクラスに定義し継承できれば楽なのですが、乗り物ごとに走行方法が違うため、Vehicleクラスでは処理内容を定めることができません。

```java:Vehicle.java
class Vehicle {
 public void run(int distance) {
  //乗り物ごとに処理が異なるため、内容を定められない
 }
}
```

そのため、処理が未定のメソッドを定義する方法があります。下図のようにメソッドの先頭に**abstract**をつけることで、**抽象メソッド**というメソッドを定義できます。抽象メソッドには中身の処理は書きません。

```java:Vehicle.java
class Vehicle {
 abstract public void run(int distance);
}
```

抽象メソッドは、**サブクラスがそのメソッドをオーバーライドしていなければエラーになります。**つまり、サブクラスがそのメソッドを**オーバーライドし、処理内容を定義することを強制する**ことになります。サブクラスに、あるメソッドを必ず持たせたいという場合は、スーパークラスに抽象メソッドとして定義しておくことが大事です。

もう一つ注意しなければならないのが、抽象メソッドを1つでも持つクラスは、**「抽象クラス」**と呼ばれ、クラス名の前に**abstract**をつける必要があります。抽象クラスはインスタンスを生成できません。抽象メソッドという未完成のメソッドを持つクラスは、それもまた未完成です。そのような未完成のクラスからはインスタンスを生成できないような仕組みになっています。

```java:Vehicle.java
abstract class Vehicle {
 abstract public void run(int distance);
}
```

それでは、実際にVehicleクラスを抽象クラスにし、runメソッドもVehicleクラスの中で抽象メソッドとして定義していきます。

```java:Vehicle.java
abstract class Vehicle {
 private String name;
 private String color;
 protected int distance = 0;        

 Vehicle(String name, String color) {
  this.name = name;
  this.color = color;

 public String getName() {
  return this.name;
 }
 public String getColor() {
  return this.color;
 }
 public int getDistance() {                             
  return this.distance;
 }
 public void setName(String name) {                     
  this.name = name;
 }
 public void setColor(String color) {                  
  this.color = color;
 }
 public void printData() {
  System.out.println("名前：" + this.name);
  System.out.println("色：" + this.color);
  System.out.println("走行距離：" + this.distance + "km");
 }
 
 //抽象メソッドrunを定義
 abstract public void run(int distance);
}
```

今回の内容はここまでとします！
次章では、「【18章】Javaを学ぼう」で完成させた自己紹介プログラムのPersonクラスと組み合わせて、今回まで作成してきたプログラムに、乗り物をある人間が所有している状況を表現してみたいと思います。
最後までご覧いただきまして、ありがとうございました！