【22章】Javaを学ぼう

今回の記事では、「【21章】Javaを学ぼう」までのプログラムの内容に、「【18章】Javaを学ぼう」で完成させた自己紹介プログラムのPersonクラスと組み合わせて、乗り物をある人間が所有している状況を表現してみたいと思います。
私自身のアウトプットの場となりますので、よろしくお願いいたします！

#前提条件（これまでの復習）
これまで学習してきた内容を載せておきます。今回はJavaを学ぼうの【1章〜21章】の知識が必要になります。

```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();
  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();
  bicycle.run(bicycleDistance);                    
 }
}
```

```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");
 }
 
 abstract public void run(int distance);
}
```

```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 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);
 }

 public void run(int distance) {
  System.out.println(distance + "km走ります");
  this.distance += distance;
  System.out.println("走行距離：" + this.distance + "km");
 }
}
```

今回組み合わせるPersonクラスは下図の通りです。

```java:Person.java
class Person {
 private String firstName;
 private String middleName;
 private String lastName;
 private int age;
 private double height;
 private double weight;

 Person(String firstName, String lastName, int age, double height, double weight) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
  this.height = height;
  this.weight = weight;
 }

 Person(String firstName, String middleName, String lastName, int age, double height, double weight) {
  this(firstName, lastName, age, height, weight);
  this.middleName = middleName;
 }

 public String fullName() {
  if (this.middleName == null) {
    return this.firstName + " " + this.lastName;
  } else {
    return this.firstName + " " + this.middleName + " " + this.lastName;
  }
 }

 public void printData() {
  System.out.println("名前は" + this.fullName() + "です");
  System.out.println("年齢は" + this.age + "歳です");
  System.out.println("身長は" + this.height + "mです");
  System.out.println("体重は" + this.weight + "kgです");
  System.out.println("BMIは" + Math.round(this.bmi()) + "です");
 }

 public double bmi() {
  return this.weight / this.height / this.height;
 }
}
```

#クラス型のフィールド
Personクラスと組み合わせて、乗り物をある人間が所有している状況をプログラムで表現できるように定義していきます。
インスタンスフィールドにクラス型の変数を定義することで、フィールドにインスタンスを持つことが可能です。

```java:Person.java
class Person {
    //クラス名
 public static int count = 0;
 public String firstName;
 public String middleName; 
 public String lastName;
   .
   .
   .
}
```

```java:Vehicle.java
abstract class Vehicle {
 private String name;
 private String color;
 protected int distance = 0; 
 private Person owner;       //Person型のownerフィールドを追加
       //クラス名       
   .
   .
   .
}
```

上図のようにPerson型のownerというインスタンスフィールドを持つようしました。
さらにprivateで定義しているため、ゲッターとセッターを定義しておきます。ゲッターの戻り値の型と、セッターの仮引数の型がクラス型になることに注意してください。

```java:Vehicle.java
abstract class Vehicle {
   .
   . 
 private Person owner;         
   .
   .
 public Person getOwner() {
  return this.owner;
 }

 public void setOwner(Person person) {
  this.owner = person;
 }
}
```

##インスタンスの戻り値
CarクラスのインスタンスやBicycleクラスのインスタンスに対してgetOwnerメソッドを呼び出すと、その戻り値はownerフィールドの値、すなわちPersonクラスのインスタンスとなっています。よって、getOwnerメソッドのあとにそのまま続けてPersonクラスのインスタンスメソッドprintDataを呼びだすことが可能です。

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

class Main {
 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);

  Person person1 = new Person("Kate", "Jones", 27, 1.6, 50.0);
  Person person2 = new Person("John", "Christopher", "Smith", 65, 1.75, 80.0);

  Car car = new Car("フェラーリ", "赤");
  //setOwnerを用いて、carの所有者をperson1に
  car.setOwner(person1);
  Bicycle bicycle = new Bicycle("ジオス", "青");
  //setOwnerを用いて、bicycleの所有者をperson2に
  bicycle.setOwner(person2);

  System.out.println("【車の情報】");         
  car.printData(); 
  System.out.println("-----------------");
  System.out.println("【車の所有者の情報】");
  // getOwnerメソッドを用いてcarのownerを取得し、
  // さらにprintDataメソッドを用いてownerの情報を出力してください
  car.getOwner().printData();
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int carDistance = scanner.nextInt();
  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.println("【自転車の所有者の情報】");
  // getOwnerメソッドを用いてbicycleのownerを取得し、
  // さらにprintDataメソッドを用いてownerの情報を出力してください
  bicycle.getOwner().printData();
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int bicycleDistance = scanner.nextInt();
  bicycle.run(bicycleDistance);                    
 }
}
```

```java:Person.java
class Person {
 private String firstName;
 private String middleName;
 private String lastName;
   .
   .
 public void printData() {
  System.out.println("名前は" + this.fullName() + "です");
  System.out.println("年齢は" + this.age + "歳です");
  System.out.println("身長は" + this.height + "mです");
  System.out.println("体重は" + this.weight + "kgです");
  System.out.println("BMIは" + Math.round(this.bmi()) + "です");
 }
   .
   .
}
```

##buyメソッドを定義
Personクラスのインスタンスが乗り物を購入できるように、buyメソッドを定義しましょう。
購入する乗り物はCarクラスのインスタンスかBicycleクラスのインスタンスです。buyメソッドの引数はどちらの型のインスタンスも受け取る可能性がありますので、下図のようにオーバーロードする必要があります。

```java:Person.java
class Person {
   .
   .
 public void buy(Car car) {
  car.setOwner(this);    //thisはbuyメソッドを呼び出している、Personクラスのインスタンスを指す
 }
 public void buy(Bicycle bicycle) {
  bicycle.setOwner(this);    //thisはbuyメソッドを呼び出している、Personクラスのインスタンスを指す
   .
   .
}
```

```java:Vehicle.java
abstract class Vehicle {
   .
   . 
 private Person owner;         
   .
   .
 public void setOwner(Person person) {
  this.owner = person;
 }
}
```

buyメソッドの中で、引数に受け取ったインスタンスのセッターを用いて所有者を変更してあげます。

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

class Main {
 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);

  Person person1 = new Person("Kate", "Jones", 27, 1.6, 50.0);
  Person person2 = new Person("John", "Christopher", "Smith", 65, 1.75, 80.0);

  Car car = new Car("フェラーリ", "赤");
  // buyメソッドを用いて、person1にcarを購入させる
  person1.buy(car);
  Bicycle bicycle = new Bicycle("ジオス", "青");
  // buyメソッドを用いて、person2にbicycleを購入させる
  person2.buy(bicycle);

  System.out.println("【車の情報】");         
  car.printData(); 
  System.out.println("-----------------");
  System.out.println("【車の所有者の情報】");
  car.getOwner().printData();
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int carDistance = scanner.nextInt();
  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.println("【自転車の所有者の情報】");
  bicycle.getOwner().printData();
  System.out.println("-----------------");
  System.out.print("走る距離を入力してください：");
  int bicycleDistance = scanner.nextInt();
  bicycle.run(bicycleDistance);                    
 }
}
```

##buyメソッドの重複
もし、Vehicleクラスのサブクラスが今後増えた場合、毎回引数の型が違う同名のメソッドを定義していかなければなりません。この問題を解決するため、引数としてVehicle型のインスタンスを受け取るようにすることで、Carクラスのインスタンスも、Bicycleクラスのインスタンスも受け取ることができるようになります。

```java:Person.java
class Person {
   .
   .
 public void Owner(Vehicle vehicle) {
  vehicle.setOwner(this);

 //public void buy(Car car) {
  //car.setOwner(this);    
 //}
 //public void buy(Bicycle bicycle) {
  //bicycle.setOwner(this);    
   .
   .
}
```

これでプログラムが完成しました！
最後までご覧いただきまして、ありがとうございました！