こんにちは、運営者のハックです。
今回はデイトラJava Silver対策編「第7章の対策」のうち、前編の学習記録を紹介します。
第7章 クラスの継承、インターフェース、抽象クラス
第7章ではクラスの継承、インターフェース、抽象クラスについて学習しますが、前編の今回は「クラスの継承」「インタフェース」「抽象クラスと具象クラス」について解いていきます。
今回も引き続き解説の参考と例題の引用は【黒本】と呼ばれる「徹底攻略Java SE 11 Silver問題集」から行っていきます。
今回の内容はJavaコース初級 基礎編Day13「クラス、インタフェース、メソッド、継承、多態性」の回で紹介している内容と重なっているから、併せて確認してほしいのにゃ。
クラスの継承
クラスの継承とは、あるクラス(親クラスまたはスーパークラス)の機能を別のクラス(子クラスまたはサブクラス)が受け継ぐことです。クラスの継承についてはJavaコース初級 基礎編Day13「クラス、インタフェース、メソッド、継承、多態性」の回で説明していますが、今回はより詳細なルールについて学習します。
クラスの継承のルール
親クラスと子クラス
- 親クラス(スーパークラス、基底クラス)は、基本的な機能を提供するクラスです。
- 子クラス(サブクラス、派生クラス)は、親クラスの機能を継承して、新しい機能を追加したり、既存の機能を変更したりします。
継承の構文
- Javaではextendsキーワードを使って継承を行います。
構文は次の通りです。
class 子クラス名 extends 親クラス名 {
// 子クラスの追加機能
}
親クラスのメンバー(フィールドとメソッド)
- 子クラスは親クラスのすべてのpublicとprotectedのメンバーを継承します。
- privateなメンバーは子クラスからは直接アクセスできません。
コンストラクタ
- 子クラスは親クラスのコンストラクタを継承しません。
- 子クラスのコンストラクタでは、親クラスのコンストラクタをsuperキーワードを使って呼び出すことができます。
オーバーライド
- 子クラスは親クラスのメソッドを再定義(オーバーライド)することができます。
- オーバーライドする場合、メソッドの名前、引数、戻り値の型が同じである必要があります。
オーバーライドについては次回で詳しく学習します。
それでは、例題を解いて理解を深めましょう!
次のプログラムを確認してください。
1. public class Child extends Parent { 2. Child() { 3. name = "java"; 4. } 5. void hello() { 6. System.out.println("hello, " + name); 7. } 8. }
このクラスが継承しているParentクラスの説明として、正しいものを選びなさい。(1つ選択)
A. Parentクラスは、helloメソッドの定義を持っていなければいけない。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. Parentクラスには、フィールドを初期化するためのコンストラクタを定義しなければいけない。
C. Parentクラスには、helloフィールドを定義しなければいけない。
D. Parentクラスには、nameフィールドを定義しなければいけない。
あにゃあ…改めて聞かれると答えられないのにゃあ。
えぇと、Childクラスに定義されていないものはParentクラスで定義していないといけないのにゃ。上の説明で「子クラスは親クラスのコンストラクタを継承しません。」ってあるから、逆に言うと子クラスには親クラスのコンストラクタは影響しないということなのにゃ。だから選択肢Bは誤りにゃ。
あと、選択肢Cは「helloフィールドを定義しなければいけない」ってあるけど、Childクラスにはhelloフィールドは無くてメソッドとして定義されているから誤りだと思うのにゃ。
nameフィールドがコンストラクタとメソッドで使われてるから、正解の選択肢はDだと思うんだけど、選択肢Aがダメな理由がイマイチ分からないのにゃ。
確かに選択肢Dが正解です。nameフィールドがParentクラスに存在しなければコンパイルエラーになります。
選択肢Aが誤りである理由はhelloメソッドはChildクラスで定義されており、Parentクラスにはhelloメソッドが必要ないからです。具体的に言うと、ChildクラスのhelloメソッドはParentクラスから継承されたものではなく、Childクラス自体に定義されています。
Javaの継承において、子クラスは親クラスのメソッドやフィールドを継承しますが、親クラスは子クラスのメソッドを定義する必要はありません。なのでChildクラスで定義されたhelloメソッドは、そのまま使用できます。
インタフェース
インタフェースは、クラスが実装すべきメソッドを定義するフレームワークであり、クラスに特定のメソッドを実装するように強制するための契約のようなものです。インタフェースについてはJavaコース初級 基礎編Day13「クラス、インタフェース、メソッド、継承、多態性」の回で説明していますが、今回はより詳細なルールについて学習します。
インタフェースの特徴とルール
メソッドの宣言のみ
インタフェースにはメソッドの名前、引数、戻り値の型だけを宣言します。具体的な内容(=実装)は書きません。
public interface Animal {
void eat();
void sleep();
}
ここでは、eatとsleepというメソッドが宣言されていますが、内容は書かれていません。
実装を持たない
インタフェースは、メソッドの実装(中身)を持ちません。実際の動作は、このインタフェースを実装するクラスで定義します。
複数のインタフェースを実装できる
クラスは複数のインタフェースを実装することができます。これは「多重実現」と呼ばれる機能の一部です。例を挙げると以下のようなコードになります。
public interface Flyer {
void fly();
}
public interface Swimmer {
void swim();
}
public class Duck implements Animal, Flyer, Swimmer {
public void eat() {
System.out.println("Duck is eating.");
}
public void sleep() {
System.out.println("Duck is sleeping.");
}
public void fly() {
System.out.println("Duck is flying.");
}
public void swim() {
System.out.println("Duck is swimming.");
}
}
定数を持てる
インタフェースには、定数(static finalなフィールド)を定義できます。定数は変更できない値です。
public interface Constants {
int MAX_VALUE = 100;
String DEFAULT_NAME = "Unknown";
}
デフォルトメソッド
デフォルトメソッド(default method)は、Java 8で導入されたインタフェースの新しい機能です。インタフェースにメソッドの実装を含めることができるようになり、インタフェースの進化を柔軟にするための仕組みです。
public interface Animal {
void eat();
default void sleep() {
System.out.println("Sleeping...");
}
}
public class Dog implements Animal {
public void eat() {
System.out.println("Dog is eating.");
}
}
public class Cat implements Animal {
public void eat() {
System.out.println("Cat is eating.");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.eat(); // "Dog is eating."
myDog.sleep(); // "Sleeping..."
myCat.eat(); // "Cat is eating."
myCat.sleep(); // "Sleeping..."
}
}
この例では、DogとCatクラスがAnimalインタフェースを実装していますが、sleepメソッドを実装していません。それでも、Animalインタフェースにデフォルトメソッドとしてsleepが定義されているため、両方のクラスでsleepメソッドを使うことができます。
デフォルトメソッドのオーバーライド
クラスでデフォルトメソッドをオーバーライドすることも可能です。つまり、クラス側でデフォルトメソッドの具体的な実装を上書きすることができます。
public class Dog implements Animal {
public void eat() {
System.out.println("Dog is eating.");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping in its kennel.");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.eat(); // "Dog is eating."
myDog.sleep(); // "Dog is sleeping in its kennel."
}
}
この例では、DogクラスがAnimalインタフェースのsleepデフォルトメソッドをオーバーライドしています。これにより、myDog.sleep()を呼び出すと、Dogクラスの実装が実行されます。
それでは、デフォルトメソッドについての例題を解いてみましょう。
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public interface A{ 2. @Override 3. default String toString(){ 4. return "A"; 5. } 6. }
1. public class B implements A { 2. @Override 3. public String toString(){ 4. return "B"; 5. } 6. }
1. public class Main { 2. public static void main(String[] args){ 3. A a = new B(); 4. System.out.println(a); 5. } 6. }
A. Aインターフェースでコンパイルエラーとなる。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. Bクラスでコンパイルエラーとなる。
C. Aが表示される。
D. Bが表示される。
E. 実行時に例外がスローされる。
デフォルトメソッドのオーバーライドの問題だにゃ。
BインターフェースはAを継承しているから、Mainクラスのaを実行するとBが表示されると思うのにゃ!だから正解は選択肢Dだにゃ!
そう思いますよね。ChatGPTもDが正解であると回答しました。
ですが、正解はなんと選択肢Aです。
この問題において、重要なポイントはObjectクラスのメソッドをインターフェースでデフォルトメソッドとしてオーバーライドすることができない、というJavaの仕様です。Java.lang.Objectクラスに定義されているメソッドをインターフェースでデフォルトメソッドとしてオーバーライドするとコンパイルエラーとなります。。「toString」メソッドがJava.lang.Objectクラスに定義されているため、Aインターフェースで「default method toString in interface A overrides a member of java.lang.Object」というコンパイルエラーとなります。
ChatGPTも間違えることがあるんだにゃ…
ところで、Java.lang.Objectクラスって何にゃあ?
Java.lang.Objectクラスは、Javaプログラムのすべてのクラスの親となる基本クラスです。すべてのJavaクラスは暗黙的にObjectクラスを継承しています。クラスを定義する際に明示的に親クラスを指定しない場合、自動的にObjectクラスを継承します。
Objectクラスには、すべてのクラスで利用できる共通メソッドが定義されています。例えば、以下のようなメソッドがあります。
- toString(): オブジェクトの文字列表現を返す。
- equals(Object obj): オブジェクトが等しいかを比較する。
- hashCode(): オブジェクトのハッシュコードを返す。
- getClass(): オブジェクトのクラス情報を取得する。
- clone(): オブジェクトのクローンを作成する(浅いコピー)。
- finalize(): ガベージコレクションがオブジェクトを破棄する直前に呼ばれる。
抽象クラスと具象クラス
抽象クラスとは完全な実装を持たず、サブクラスに継承されて具体的に実装されるクラスであり、具象クラスとは完全な実装を持ち、直接インスタンス化できるクラスのことです。
抽象クラスについてはJavaコース初級 基礎編Day13「クラス、インタフェース、メソッド、継承、多態性」の回で詳しく説明していますので、今回は具象クラスについて説明します。
具象クラス
具象クラスは、Javaの中で普通のクラスと呼ばれるもので、直接インスタンス(オブジェクト)を作成できるクラスです。
以下に具象クラスのルールを紹介します。
完全な実装を持つ
具象クラスはすべてのメソッドを完全に実装しなければなりません。つまり、メソッドの具体的な動作が書かれている必要があります。例えば、次のように具象クラスを定義します。このクラスでは、barkというメソッドが完全に実装されています。
public class Dog {
public void bark() {
System.out.println("ワンワン");
}
}
なお、抽象クラスは一部またはすべてのメソッドが実装されていない(抽象メソッド)ことがあります。
インスタンス化できる
具象クラスは直接インスタンス化できます。インスタンス化とは、クラスを元に具体的なオブジェクトを作ることです。例えば、次のようにインスタンス化します。
Dog myDog = new Dog();
myDog.bark(); // 出力: ワンワン
なお、抽象クラスは直接インスタンス化できません。つまり、new AbstractAnimal()のように使うことはできません。
コンストラクタがある
具象クラスにはコンストラクタ(オブジェクトを初期化するための特別なメソッド)が必要です。コンストラクタはクラスと同じ名前を持ちます。例えば、次のようにコンストラクタを定義します。
public class Dog {
public Dog() {
// ここで初期化の処理が書けます
}
public void bark() {
System.out.println("ワンワン");
}
}
なお、抽象クラスにもコンストラクタはありますが、それは抽象クラスを継承した具象クラスから呼び出されます。
public abstract class AbstractAnimal {
public AbstractAnimal() {
// 初期化コード
}
}
継承できる
具象クラスは他のクラスを継承することができます。また、他のクラスに継承されることもできます。継承とは、あるクラスが別のクラスの性質やメソッドを引き継ぐことです。例えば、次のように継承します。
public class Animal {
public void eat() {
System.out.println("食べる");
}
}
public class Dog extends Animal {
public void bark() {
System.out.println("ワンワン");
}
}
Dog myDog = new Dog();
myDog.eat(); // 出力: 食べる
myDog.bark(); // 出力: ワンワン
ちなみに、抽象クラスは他のクラスに継承されることを前提としています。抽象クラスの継承は具象クラスだけでなく、抽象クラスでも可能です。継承するクラスで未実装のメソッドを実装する必要があります。
public class Dog extends AbstractAnimal {
@Override
public void makeSound() {
System.out.println("ワンワン");
}
}
長々と説明しましたが、最初に説明したとおり具象クラスとは「普通のクラス」のことです。
今まで学んできたことの復習という感じでしたね。
それでは、抽象クラスと具象クラスについて例題を解いて、きちんと理解しているか確認しましょう。
抽象クラスに関する説明として、正しいものを選びなさい。(3つ選択)
A. インスタンスを生成することはできない。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 抽象クラスのメソッドはオーバーライドできない。
C. サブクラスから抽象クラスの公開フィールドに自由にアクセスできる。
D. 抽象クラスを継承した抽象クラスを定義できる。
E. 抽象メソッドは、すべてのサブクラスが実装しなければならない。
さっきの説明で、抽象メソッドはインスタンスを生成できないって書いてあったから選択肢Aは正解だにゃ!。選択肢Dもさっきの説明で「抽象クラスの継承は具象クラスだけでなく、抽象クラスでも可能」って言っていたから正解だにゃ!
選択肢Bはさっきの説明でオーバーライドしていたコードがあったから、誤りなのにゃ。
選択肢Eの「すべてのサブクラス」ってことは抽象クラスを継承した抽象クラスも含まれているのにゃ?抽象クラスは実装を提供しなくてもいいから選択肢Eは誤りっぽいのにゃ。
消去法で選択肢Cが正解だとおもうんだけど、理由がよく分からないから説明よろしくにゃ。
ねこ奈の言う通り正解は選択肢A、C、Dです。
選択肢Cについて、「サブクラスから抽象クラスの公開フィールドに自由にアクセスできる」とありますが、これはスーパークラス(親クラス)とサブクラス(子クラス)のアクセス権限の関係に関わっています。
選択肢Cの「サブクラスから抽象クラスの公開フィールドに自由にアクセスできる」は、スーパークラスのpublicおよびprotectedフィールドがサブクラスからアクセス可能であることを意味します。これは、スーパークラスが抽象クラスであるかどうかに関係なく適用されるルールです。従って、選択肢Cは正しいといえます。
中編に続きます
今回は「クラスの継承、インターフェース、抽象クラス」について、「クラスの継承」「インタフェース」「抽象クラスと具象クラス」について解きました。
第7章の内容はデイトラ初級編でも最難関と言われていた内容です。
内容を詳しく説明したため想定よりも文章量が多くなってしまい、前章と同じく3部構成で作成することにしました。その分内容を深掘りして学習し、当ブログへアウトプットを行っていきます。
以上で今回の学習記録を終えます。
ここまでご覧いただきありがとうございました。
コメント