はじめに
最近Java基礎の研修を受けました。学生時代にJavaを授業で習って基礎は身につけていたのですが、忘れていた内容もあったので備忘録として書き残してみます。
この記事では、Javaの基礎的な内容に関してまとめていきます。
今回は継承です。
継承とは
継承とは、クラスの機能を受け継いで機能を拡張した新しいクラスを作成する方法です。
このとき、継承元となるクラスを親クラス(スーパークラス)、継承して新たに作成したクラスを子クラス(サブクラス) と呼びます。
次の関係を見てみましょう。
- 商品
- 本
- 食材
このとき、「商品」という親クラスから、「本」「食材」という子クラスが作成されています。
継承の方法
継承は以下のように行います。
class 子クラス extends 親クラス名 {
}
継承の例を見てみましょう。
class Item {
// 「商品」クラス
}
class Book extends Item {
// 「本」クラス
}
class Food extends Item {
// 「食材」クラス
}
継承の注意
継承を利用するときは、以下の点に気をつけましょう。
- 継承できるクラスは一つ
- 継承したクラスをさらに継承できる
- 親クラスから子クラスのメンバは参照できない
(参考)Objectクラス
全てのクラスは暗黙的にObjectクラスを継承します。全てのクラスの始祖はObjectクラスなのです。
Objectクラスには、全てのクラスが持つべきメソッドが宣言されています。
- toStringメソッド
- オブジェクトの文字列表現を返す
- equalsメソッド
- 2つのインスタンスが等しいかどうかを示す
全てのクラスはtoStringメソッドもequalsメソッドも呼び出せます。
オーバーライド
オーバーライドとは、子クラスで親クラスのメソッドを上書きすることです。
ObjectクラスのtoStringメソッドをオーバーライドしてみましょう。
class Item {
private String name;
private int price;
public void setName(){ 省略 }
public void setPrice(){ 省略 }
@Override
public String toString() {
return name + "(" + price + "円)";
}
}
public static void main(String[] args) {
Item item = New Item();
item.setName("Qiita攻略本");
item.setPrice(1000);
String str = item.toString();
System.out.println(str); // Qiita攻略本(1000円)
}
オーバーライドしたメソッドの中でsuper.メソッド名(引数リスト)
と記述することで、親クラスのメソッドを呼び出すこともできます。
抽象メソッドと抽象クラス
オーバーライドを前提としたメソッドを抽象メソッド、抽象メソッドが宣言されているクラスを抽象クラスと呼びます。対比として、抽象メソッドを持たないクラスを具象クラスと呼ぶこともあります。
抽象クラスは以下の特徴があります。
- インスタンスを作成できない
- 抽象クラスは継承され、抽象メソッドをオーバーライドされる前提で作られる
- 抽象クラスを継承したクラスは抽象メソッドを全てオーバーライドしないとコンパイルエラーとなる
抽象クラスと抽象メソッドの宣言
抽象メソッドは、戻り値の型の前にabstract
をつけて宣言します。また、処理の中身を持たないので{}
の代わりに;
を記述します。
抽象メソッドは以下のように記述します。
修飾子 abstract 戻り値の型 メソッド名(引数リスト);
抽象クラスの抽象メソッドと同様に、class
の前にabstract
をつけて宣言します。抽象メソッド以外のメンバも宣言できます。
修飾子 abstract class クラス名 {
抽象メソッドなどのメンバ
}
抽象クラスと具象クラスの例を見てみましょう。
abstract class Page {
public abstract String getTitle();
public void printReview() {
System.out.println("title : " + getTitle() );
System.out.println("review : It was really great story.");
}
}
class ReviewPage extends Page {
@Override
public abstract String getTitle() {
return "The Story Of Qiita";
}
}
public static void main(String[] args) {
ReviewPage page = New ReviewPage();
page.printReview();
}
title : The Story Of Qiita
review : It was really great story.
コンストラクタ
具象クラスと抽象クラスのどちらを継承した場合も、親クラスのコンストラクタは、子クラスに引き継がれません。
class Item {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
}
class Book extends Item {
// メンバを宣言していない
}
public static void main(String[] args) {
// Book book = new Book("Qiita攻略", 1000); // コンパイルエラー
}
親クラスのコンストラクタを呼び出すには、子クラスのコンストラクタ内でsuper(引数リスト)
と記述します。
class Book extends Item {
public Book(String name, int price) {
super(name, price); // 親クラスのコンストラクタを呼び出す
}
}
public static void main(String[] args) {
Book book = new Book("Qiita攻略", 1000); // コンパイルエラーは起こらない
}
通常、子クラスのコンストラクタは、暗黙的に親クラスの引数なしコンストラクタを呼び出します。親クラスに、引数1個以上のコンストラクタのみが宣言されている場合は、明示的に呼び出す必要があります。
継承したクラスのインスタンス
親クラスの型の変数には、子クラスのインスタンスを代入できます。
※逆は成り立ちません。
Item book = new Book();
Page page = new ReviewPage();
親クラス型の変数に子クラスのインスタンスを代入するときは、呼び出せるメソッドと呼び出し結果に注意が必要です。なぜなら、コンパイル時と実行時で、参照している型が異なるからです。
- コンパイル時:変数の型を参照し、メソッドが実行できるかが決まる
- 実行時:インスタンスの型を参照し、メソッドの実行内容が決まる
まとめ
初めのうちは、クラスにも親子関係が作れて、中身をアップデートできるんだな〜と思ってくれれば良いと思います。細かい制約がありますが、徐々に覚えていきましょう!