継承
クラスを定義する際に、クラス名+extends
+基底クラス名とすることでクラスを継承し、その機能を使うことができる。
superキーワード
子クラスから親クラスのメソッドやフィールド、コンストラクタへアクセスするために用いる。
共通する処理は親クラスのメソッドに記述し、super
で参照する。
例
public class Sample {
public static void main(String[] args) {
GasolineCar g = new GasolineCar("赤");
HybridCar h = new HybridCar("白");
g.drive(1.0);
h.drive(2.0);
}
}
//基底クラス
public class Car {
double distance = 0.0; //総走行距離
String color = "";
//コンストラクタ
public Car(String color) {
this.color = color;
}
public void drive(double d) {
distance +=d;
}
}
// extendsで継承
public class GasolineCar extends Car{
public GasolineCar(String color) {
super(color);//親クラスCarのコンストラクタを参照
}
public void drive(double d) {
super.drive(d);//親クラスのdriveメソッドを呼び出す
System.out.println("-----------------------");
System.out.println("ぶーん");
System.out.println("ガソリンを消費して"+d+"km走行しました。");
System.out.println("マイレージは、"+distance+"kmです。");
}
}
public class HybridCar extends Car {
public HybridCar(String color) {
super(color);
}
public void drive(double d) {
super.drive(d);
System.out.println("-----------------------");
System.out.println("すいーん");
System.out.println(d+"km走行しました。燃費もいいですよ");
System.out.println("マイレージは、"+distance+"kmです。");
}
}
抽象クラスと抽象メソッド
抽象化インスタンス化できないクラスで、abstract
を先頭につけて定義する。
継承してより具体性の高いクラスを作らないと使用できない。
抽象メソッドは、メソッド名や受け取るパラメータ、返す値の型は書かれているが、処理ブロックは書かれていないメソッドのことである。継承したクラスで実装し、処理を記述する。
抽象メソッドを1つでも持つクラスは抽象クラスであり、クラス名の前にabstractをつける必要がある。
サブクラスにあるメソッドを必ず持たせたいという場合は、スーパークラスに抽象メソッドとして定義しておく。
例
public class Sample {
public static void main(String[] args) {
Praus p = new Praus("グレー");
p.drive(1.0);
p.drive(5.0);
p.drive(100.0);
}
}
//抽象化クラスの定義
abstract public class Car {
double distance = 0.0; // 総走行距離
String color = "";
public Car(String color) {
this.color = color;
}
public void drive(double d) {
distance += d;
}
}
abstract public class GasolineCar extends Car {
public GasolineCar(String color) {
super(color);
}
public void drive(double d) {
super.drive(d);
this.useGasoline(d);
}
// 抽象メソッドの定義
abstract void useGasoline(double d);
}
abstract public class HybridCar extends Car {
public HybridCar(String color) {
super(color);
}
public void drive(double d) {
super.drive(d);
this.useEnergy(d);
}
abstract void useEnergy(double d);
}
//抽象クラスの継承
public class Praus extends HybridCar {
public Praus(String color) {
super(color);
}
//抽象メソッドの実装
@Override
void useEnergy(double d) {
System.out.println(d + "km走行するための燃料を使いました。");
}
@Override
public void drive(double d) {
System.out.println("--------------------------------");
System.out.println(this.color + "のプラウスが走っています。");
super.drive(d);//親クラスであるHybridCarクラスのdriveメソッドが実行される
}
}
--------------------------------
グレーのプラウスが走っています。
1.0km走行するための燃料を使いました。
--------------------------------
グレーのプラウスが走っています。
5.0km走行するための燃料を使いました。
--------------------------------
グレーのプラウスが走っています。
100.0km走行するための燃料を使いました。
カプセル化
オブジェクト内のクラスやメソッド、フィールドに対して外部からアクセスできないようにすること。クラス間の依存関係を低減することができるという意義がある。
他のクラスから変更されたくないフィールドやメソッドのアクセス修飾子をprivate
修飾子にすることでカプセル化できる。継承先でアクセスしたい場合は、protected
修飾子を付ける(または何もつけない)。
フィールドはprivate
、メソッドはpublic
にするのが定石。
内部処理を知らずともそのクラスを利用することができるため、プログラムの拡張が容易になる。また、機能をAPIとしてクラス単位で提供することができるため、プログラムの構造が整理される。
ゲッターとセッターの定義
オブジェクト内部のメンバ変数に外部からアクセスするために用意されたメソッドをアクセサという。オブジェクト指向においてインスタンス内の変数などは内部の状態を表すものであり、外部から直に参照したり操作したりすべきでないという考え方に基づく。変数などへのアクセスに必ず対応するメソッドを経由することにより、メソッド内で適切にチェックや処理を行って破綻の無いように外部からのアクセスをコントロールできるという利点がある。
一方、Javaのようにpublic
宣言が用意されている言語では、特に事情がない限り外部に公開してもよい変数などは直に操作するようにしたほうが記述が完結になり、コード量や処理量も軽量になるため、必ずしもすべてにアクセサを用意する必要はないとする考え方もある。C#やSwiftのように、アクセサを自動生成して変数を直接参照するようなコードを記述すると内部的に自動でアクセサ経由にしてくれる言語もある。
ゲッターを生成すると、カプセル化されたフィールドを外部から参照できるようになる。
セッターを生成すると、カプセル化されたフィールドを外部から変更できるようになる。
public class Person {
final static int MAN = 1;
final static int WOMAN = 2;
private String name;//メンバ変数のカプセル化
private int age;
private int sex;
public Person(String name,int age,int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// ゲッターの生成
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getSex() {
return sex;
}
// セッターの生成
public void setAge(int age) {
if (age<0) {
System.out.println("マイナスの年齢をセットできません");
return;
}
this.age = age;
}
多態性
ポリフォーリズムとも呼ばれる概念で、サブクラスのインスタンスはスーパークラスのクラス型変数に代入できる。これにより同じ内容を持つメソッドを、親クラスから呼び出すことでコードをシンプルにすることができる。
メソッドの名前が同じで、パラメータの種類だけが違うものをメソッドのオーバーロードというが、これを一つのメソッドで記述する。
instanceof演算子
あるオブジェクトの型を動的に判定するための演算子。
以下の判定などに用いる。
- オブジェクトが、あるクラスのインスタンスか
- オブジェクトが、あるクラスの子クラスのインスタンスか
- オブジェクトが、特定のインターフェースを実装したインスタンスか
オブジェクト instanceof 型
例1
public class Sample {
public static void main(String[] args) {
Praus praus = new Praus("グレー");
FlyingRandyCruiser randy = new FlyingRandyCruiser("白");
FlyingPerson person = new FlyingPerson("太郎", 8, Person.MAN);
driveThreeTimes(praus);
makeItFlyThreeTimes(randy);
}
// Praus,FlyingRandyCruiserの親クラスCarをパラメータに設定
static void driveThreeTimes(Car car) {
car.drive(1.0);
car.drive(2.0);
car.drive(3.0);
// HybridCarを継承したインスタンスなら実行
if (car instanceof HybridCar) {
System.out.println("環境に優しいですね");
}
}
// FlyingPerson,FlyingRandyCruiserが共に実装しているFlyableインターフェイスをパラメータに設定
static void makeItFlyThreeTimes(Flyable obj) {
obj.fly();
obj.fly();
obj.fly();
}
}
1.0km走行するための燃料を使いました。
--------------------------------
グレーのプラウスが走っています。
2.0km走行するための燃料を使いました。
--------------------------------
グレーのプラウスが走っています。
3.0km走行するための燃料を使いました。
環境に優しいですね
おお!ランディクルーザーが飛びました!
おお!ランディクルーザーが飛びました!
おお!ランディクルーザーが飛びました!
例2
class Main {
public static void main(String[] args) {
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("フェラーリ", "赤");
Bicycle bicycle = new Bicycle("ビアンキ", "緑");
// PersonクラスのインスタンスをVehicleクラスのインスタンス変数ownerにセット
person1.buy(car);
person2.buy(bicycle);
System.out.println("【車の情報】");
car.printData();
System.out.println("-----------------");
System.out.println("【車の所有者の情報】");
car.getOwner().printData(); // getOwnerメソッドで返されるPersonクラスのインスタンスに対してprintDataメソッドを呼び出す
System.out.println("=================");
System.out.println("【自転車の情報】");
bicycle.printData();
System.out.println("-----------------");
System.out.println("【自転車の所有者の情報】");
bicycle.getOwner().printData();
}
}
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;
}
// Vehicle型の値を値を受け取るbuyメソッド
public void buy(Vehicle vehicle) {
// buyメソッドを呼び出しているPersonクラスのインスタンスをVehicleクラスのインスタンス変数ownerにセット
vehicle.setOwner(this);
}
}
abstract class Vehicle {
private String name;
private String color;
protected int distance = 0;
private Person owner;
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 Person getOwner() {
return this.owner;
}
// セッターを定義
public void setName(String name) {
this.name = name;
}
public void setColor(String color) {
this.color = color;
}
public void setOwner(Person person) {
this.owner = person;
}
// 名前、色、走行距離を出力するメソッド
public void printData() {
System.out.println("名前:" + this.name);
System.out.println("色:" + this.color);
System.out.println("走行距離:" + this.distance + "km");
}
public abstract void run(int distance);
}
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("給油できません");
} else if (litre + this.fuel >= 100) {
System.out.println("満タンまで給油します");
this.fuel = 100;
} else {
this.fuel += litre;
}
System.out.println("ガソリン量:" + this.fuel + "L");
}
}
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");
}
}