Java Silver試験で頻出の「抽象クラスとインターフェース」に関する問題を10問収録しました。abstractクラス、インターフェースの定義と実装、デフォルトメソッド、staticメソッド、多重実装とダイヤモンド問題など、試験で問われやすいポイントを網羅しています。
問題1 ⭐(基本)
次のコードをコンパイル・実行すると、どうなりますか?
abstract class Shape {
abstract double area();
void describe() {
System.out.println("Area: " + area());
}
}
class Circle extends Shape {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
return Math.PI * radius * radius;
}
}
public class Main {
public static void main(String[] args) {
Shape s = new Circle(5);
s.describe();
}
}
- A.
Area: 78.53981633974483 - B.
Area: 0.0 - C. コンパイルエラー
- D. 実行時エラー
解答と解説
正解: A
抽象クラスShapeはabstractメソッドarea()と具象メソッドdescribe()を持っています。CircleはShapeを継承し、area()を実装しています。
describe()はarea()を呼び出しますが、ポリモーフィズムにより実行時のオブジェクト型Circleのarea()が呼ばれます。Math.PI * 5 * 5 = 78.53981633974483が出力されます。
抽象クラスの重要なポイント:
- 抽象クラスはインスタンス化できない(
new Shape()はコンパイルエラー) - 抽象メソッドと具象メソッドの両方を持てる
- サブクラスはすべての抽象メソッドを実装しなければならない(自身も
abstractでない限り)
問題2 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
abstract class Vehicle {
abstract void start();
Vehicle() {
System.out.println("Vehicle created");
start();
}
}
class Car extends Vehicle {
String engine = "V8";
@Override
void start() {
System.out.println("Engine: " + engine);
}
}
public class Main {
public static void main(String[] args) {
Car c = new Car();
}
}
- A.
Vehicle created→Engine: V8 - B.
Vehicle created→Engine: null - C. コンパイルエラー
- D. 実行時エラー
解答と解説
正解: B
コンストラクタの実行順序に注意が必要です:
-
new Car()でまず親クラスVehicleのコンストラクタが実行される -
"Vehicle created"が出力される -
start()が呼ばれる → ポリモーフィズムによりCarのstart()が実行される - この時点で
Carのフィールド初期化(engine = "V8")はまだ行われていない -
engineはデフォルト値nullのまま →"Engine: null"が出力される
コンストラクタからオーバーライド可能なメソッドを呼び出すと、サブクラスのフィールドがまだ初期化されていない状態でメソッドが実行される可能性があります。これは避けるべき設計パターンです。
問題3 ⭐(基本)
次のコードをコンパイル・実行すると、どうなりますか?
interface Printable {
void print();
}
interface Loggable {
void log();
}
class Document implements Printable, Loggable {
@Override
public void print() {
System.out.println("Printing document");
}
@Override
public void log() {
System.out.println("Logging document");
}
}
public class Main {
public static void main(String[] args) {
Document doc = new Document();
doc.print();
doc.log();
}
}
- A.
Printing document→Logging document - B. コンパイルエラー(複数インターフェースの実装不可)
- C. コンパイルエラー(メソッドの実装漏れ)
- D. 実行時エラー
解答と解説
正解: A
Javaではクラスの多重継承はできませんが、インターフェースは複数実装できます。DocumentはPrintableとLoggableの両方をimplementsで実装しています。
両方のインターフェースで宣言されたメソッドprint()とlog()をそれぞれ実装しているため、正常にコンパイル・実行できます。
インターフェースのメソッドを実装する際は、アクセス修飾子をpublicにする必要があります(インターフェースのメソッドは暗黙的にpublic abstractであるため)。
問題4 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
interface Greeting {
default void greet() {
System.out.println("Hello!");
}
static void info() {
System.out.println("Greeting interface v1.0");
}
}
class JapaneseGreeting implements Greeting {
@Override
public void greet() {
System.out.println("Konnichiwa!");
}
}
public class Main {
public static void main(String[] args) {
Greeting g = new JapaneseGreeting();
g.greet();
Greeting.info();
}
}
- A.
Hello!→Greeting interface v1.0 - B.
Konnichiwa!→Greeting interface v1.0 - C.
Konnichiwa!のみ - D. コンパイルエラー
解答と解説
正解: B
defaultメソッドはインターフェースに実装を持たせることができ、実装クラスでオーバーライドできます。JapaneseGreetingはgreet()をオーバーライドしているため、"Konnichiwa!"が出力されます。
staticメソッドはインターフェース名で直接呼び出します(Greeting.info())。インスタンス経由(g.info())では呼び出せません。インターフェースのstaticメソッドは継承されません。
問題5 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
interface A {
default void show() {
System.out.println("A");
}
}
interface B {
default void show() {
System.out.println("B");
}
}
class C implements A, B {
@Override
public void show() {
A.super.show();
}
}
public class Main {
public static void main(String[] args) {
C c = new C();
c.show();
}
}
- A.
A - B.
B - C. コンパイルエラー(ダイヤモンド問題)
- D.
AとBの両方
解答と解説
正解: A
2つのインターフェースが同じシグネチャのdefaultメソッドを持つ場合、実装クラスは必ずそのメソッドをオーバーライドしなければなりません(オーバーライドしないとコンパイルエラー)。
クラスCはshow()をオーバーライドし、A.super.show()で明示的にAインターフェースのデフォルトメソッドを呼び出しています。
インターフェース名.super.メソッド名()の構文で、どのインターフェースのデフォルトメソッドを呼ぶか指定できます。オーバーライドを省略すると、コンパイラがどちらを使うか判断できずコンパイルエラーになります。
問題6 ⭐⭐(応用)
次のコードをコンパイルすると、どうなりますか?
abstract class Animal {
abstract void eat();
abstract void sleep();
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("Dog eats");
}
}
- A. コンパイル成功
- B. コンパイルエラー:
Dogがsleep()を実装していない - C. コンパイルエラー:
abstractクラスにコンストラクタがない - D. コンパイルエラー:
abstractメソッドが2つ定義されている
解答と解説
正解: B
抽象クラスを継承する具象クラス(abstractでないクラス)は、すべての抽象メソッドを実装しなければなりません。Dogはeat()のみ実装していますがsleep()を実装していないため、コンパイルエラーになります。
修正方法:
-
Dogにsleep()の実装を追加する - または
Dog自体をabstract class Dog extends Animalにする(ただしインスタンス化できなくなる)
問題7 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
interface Flyable {
int MAX_ALTITUDE = 10000;
void fly();
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Flying up to " + MAX_ALTITUDE);
}
}
public class Main {
public static void main(String[] args) {
Bird b = new Bird();
b.fly();
// Flyable.MAX_ALTITUDE = 20000; // ← この行のコメントを外すとどうなる?
}
}
- A.
Flying up to 10000(コメント行を外すとコンパイルエラー) - B.
Flying up to 10000(コメント行を外すと20000に変わる) - C. コンパイルエラー
- D.
Flying up to 0
解答と解説
正解: A
インターフェースで宣言されたフィールドは暗黙的にpublic static finalです。つまりMAX_ALTITUDEは定数であり、再代入できません。
// 実質的に以下と同じ
public static final int MAX_ALTITUDE = 10000;
コメント行を外すとFlyable.MAX_ALTITUDE = 20000はfinal変数への再代入となりコンパイルエラーになります。
インターフェースの変数は常に:
-
public→ どこからでもアクセス可能 -
static→ インスタンスなしでアクセス可能 -
final→ 再代入不可
問題8 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
interface X {
default void hello() {
System.out.println("X");
}
}
interface Y extends X {
default void hello() {
System.out.println("Y");
}
}
class Z implements X, Y {
}
public class Main {
public static void main(String[] args) {
Z z = new Z();
z.hello();
}
}
- A.
X - B.
Y - C. コンパイルエラー(ダイヤモンド問題)
- D. 実行時エラー
解答と解説
正解: B
YはXを継承し、hello()をオーバーライドしています。ZはXとYの両方を実装していますが、YはXのサブインターフェースなので、より具体的なインターフェース(Y)のデフォルトメソッドが優先されます。
これは「ダイヤモンド問題」のように見えますが、YがXをextendsしているため、コンパイラはYのhello()を自動的に選択します。Zでオーバーライドする必要はありません。
もしYがXをextendsしていない(つまり独立した2つのインターフェース)場合は、Zで明示的にオーバーライドしなければコンパイルエラーになります。
問題9 ⭐(基本)
次のコードのうち、コンパイルエラーになるものはどれですか?
// 選択肢A
abstract class A1 {
abstract void method();
void concreteMethod() {
System.out.println("concrete");
}
}
// 選択肢B
abstract class B1 {
void method() {
System.out.println("not abstract");
}
}
// 選択肢C
class C1 {
abstract void method();
}
// 選択肢D
abstract class D1 {
abstract void method() {
System.out.println("has body");
}
}
- A. 選択肢Aのみ
- B. 選択肢Cのみ
- C. 選択肢CとDの両方
- D. 選択肢A, C, Dの3つ
解答と解説
正解: C
- A: 正常 — 抽象クラスは抽象メソッドと具象メソッドの両方を持てる
- B: 正常 — 抽象クラスに抽象メソッドがなくてもよい(インスタンス化の防止などに使える)
-
C: コンパイルエラー —
abstractメソッドはabstractクラス内でのみ宣言できる。C1はabstractクラスではない -
D: コンパイルエラー —
abstractメソッドは本体({}ブロック)を持てない。abstractと本体は矛盾する
問題10 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
interface Processor {
static void process() {
System.out.println("Processing in interface");
}
}
class DataProcessor implements Processor {
static void process() {
System.out.println("Processing in class");
}
}
public class Main {
public static void main(String[] args) {
Processor.process();
DataProcessor.process();
Processor p = new DataProcessor();
// p.process(); // ← この行のコメントを外すとどうなる?
}
}
- A.
Processing in interface→Processing in class(コメント行はコンパイルエラー) - B.
Processing in interface→Processing in class(コメント行はProcessing in class) - C.
Processing in class→Processing in class - D. コンパイルエラー
解答と解説
正解: A
インターフェースのstaticメソッドの重要なルール:
-
インターフェースの
staticメソッドは継承されない —DataProcessorのprocess()はインターフェースのprocess()とは無関係の新しいメソッド -
Processor.process()→ インターフェースのstaticメソッドが呼ばれる →"Processing in interface" -
DataProcessor.process()→ クラスのstaticメソッドが呼ばれる →"Processing in class" -
p.process()→pの型はProcessor(インターフェース)で、インターフェースのstaticメソッドはインスタンス経由で呼び出せないためコンパイルエラー
クラスのstaticメソッドはインスタンス経由で呼び出せます(推奨されませんが)が、インターフェースのstaticメソッドはインターフェース名でしか呼び出せません。
参考
- Oracle Java SE 17 Language Specification - Chapter 9: Interfaces
- Oracle Java Tutorials - Abstract Methods and Classes
- Oracle Java Tutorials - Interfaces
- Oracle Java Tutorials - Default Methods
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!