【Javaお勉強日記】クラスの継承についてまとめる
クラスの継承とは
本棚管理アプリを作りたいとして、基本となる「Book」クラスを作ったとする。
でも、本って「一冊で完結するもの」「シリーズもの」「雑誌」って色々な種類がある。
・タイトル、出版社、ページ数……等は全ての種類の本に共通の情報
・雑誌に「著者名」は要らないけど、●年●月号、という情報は必須
・シリーズものにはシリーズ名と巻数が要る
って具合に、「ちょっと同じだけどちょっと違う」ものを取り扱うためのクラスを作りたいときに、まず基本になる「本」オブジェクトを作り、それを取り込んで、「ちょっと違う」所だけ追加した「雑誌」「漫画」「新書」……と、色々な種類のオブジェクトを作ることができる。これが「クラスの継承」。
その時、基本になるオブジェクトを「スーパークラス」、スーパークラスを継承して作ったクラスを「サブクラス」と呼ぶ。
あるクラスを継承したクラスの作り方
// Book.java
package books;
// スーパークラスとなるBookクラスを用意
public class Book{
private String title;
private String Publisher;
public Book(String title, String publisher){
this.title = title;
this.publisher = publisher
}
public getTitle(){
return this.title
}
// 以下getPublisher略
}
// Comic.java
package books;
// Bookクラスを継承したComicクラスを作る
public class Comic extends Book{
// BookになくてComicに必要なフィールド変数を宣言する
private String author;
private String sireseTitle;
private String sireseNumber;
// コンストラクタの引数には、スーパークラスの持っているフィールド変数も取る
public Comic(String title, String publisher, String author, String sireseTitle, String sireseNumber){
// 真っ先に、スーパークラスのコンストラクタを呼ぶ(必須)
super(title, publisher);
// それから、自クラスのフィールド変数を初期化する
this.author = author;
this.sireseTitle = sireseTitle;
this.sireseNumber = sireseNumber;
}
// 以下、getAuthorとかgetSireseTitle...略
}
・サブクラスの宣言時に extends スーパークラス名 を付けると、スーパークラスを継承できる。(特にimportとか書く必要はない)
・まず、スーパークラスになく、サブクラスに必要なフィールド変数を宣言する
・それから、コンストラクタを作る。
・コンストラクタの一番最初で、Suber()を使って、スーパークラスのコンストラクタを呼び出す(スーパークラス名()、ではないので注意。また、Suber()の実行がコンストラクタ内の一番最初でないとコンパイルエラーになる)
これで、Bookクラスを継承したComicクラスが完成。
Bookクラスを継承しているので、Bookクラスにあるフィールド変数とメソッド全てを内包している。
スーパークラスのprivateメンバにはアクセス出来ない
ところが、上記の例のBookクラスのフィールド変数、titleとかpublisherはアクセス修飾子がprivateになっている。
例えBookを継承したサブクラスであっても、Comicの中でthis.titleしようとしてもアクセスできない。
えー それはちょっと不便じゃないの~~?
そんなときにはprotected
アクセス修飾子にはprivateとpublicの他にもう一つ、protectedがある。
protectedは「同じパッケージの中からならアクセスできる」「違うパッケージからの場合、そのクラスのサブクラスからならアクセスできる」という修飾子。
もちろんpublicにすればサブクラスからでもアクセスできるけど、publicにしちゃうのはちょっと怖い。そんなときにはprotectedを使うと、ほどほどにアクセスを制限できる。
でもそれ「予想外の変更」起きちゃうのでは?
起きちゃうね。
なので、安心して使える設計にするなら、サブクラスの中からでもスーパークラスのフィールド変数使いたい時は、publicにしてあるgetter使った方が安心……
ただ、現実的にどうしても理想通りの運用が出来ない場合もあるので、そんなときはprotectedで対応しよう。
スーパークラスのメソッドの呼び出し方
// Comic.java
package books;
public class Comic extends Book{
private String author;
private String sireseTitle;
private String sireseNumber;
public Comic(String title, String publisher, String author, String sireseTitle, String sireseNumber){
// コンストラクタ略
}
// 以下、getAuthorとかgetSireseTitle...も略
public void testPrint(){
// this.を使って呼ぶ
System.out.println(this.getTitle())
// super.で呼んだ方が可読性が高い
System.out.println(super.getTitle())
// なんならthis.もsuper.もなくても使えちゃう
System.out.println(getTitle())
}
}
・Bookクラスを継承しているので、インスタンス作れば「this」の中にgetTitleも入るから、「this.getTitle」で呼び出すことが出来る。
・ただ、厳密に言うとサブクラスで定義してるメソッドじゃないから、後から自分や仲間が「this?thisのどこ??」ってなりそうなので、「super.getTitle」で呼びだした方がより読みやすい。
・実は this. も super. も無くても動くんだけど、いよいよ「このメソッドどこから来た?」になるので、明示的に書きたい。
final修飾子が付いているクラスは継承できない
【Javaお勉強日記】イミュータブル(変更不能)なオブジェクトを作る|yucco|note
ここでまとめたみたいに、変更不能にするためにfinal修飾子を付けたクラスは継承できない。
Stringとかの重要なクラスは、変なサブクラス作られないように継承不可になっていたりする。
全てのクラスの基底クラスはObjectクラス
絶対に全てのクラスに必要なメソッド(例えばtoString()とか)が揃っている基底クラス。
なんの継承も宣言せずにクラスを作ると、自動的にObjectクラスのサブクラスとして生成される。普段そのことを意識する必要はそんなにないけど、「toString()メソッド作ってないのにtoString()できちゃう!」とかはこれが原因なので、あたまの片隅に入れておく。
継承周りはひとまずこれくらいで。
この記事が気に入ったらサポートをしてみませんか?