継承とポリモフィズムの要点
内容の確認日: 2026-10-03
この分野は Bronze の中でも出題数が多く、「どのクラスのメソッドが実行されるか」「そもそもコンパイルできるか」を見分ける問題が中心です。出題項目は「サブクラスの定義と使用」「メソッドのオーバーライド」「抽象クラスやインタフェースの定義と実装」「ポリモフィズムを使用するコードの作成」「参照型の型変換」「パッケージ宣言とインポート」の 6 つです。
迷ったときは、「呼び出せるかどうかは変数の型で決まり、実際に動くのはオブジェクトのクラスのメソッド」という原則に立ち返ると整理できます。
サブクラスの定義とコンストラクタ
class Car extends Vehicle のように extends で親クラスを指定すると、親クラスのフィールドとメソッドを引き継いだサブクラスになります。Java のクラスが継承できるのは1 つのクラスだけです (extends A, B はコンパイルエラー)。extends を書かないクラスは、自動的に java.lang.Object を継承します。
サブクラスのコンストラクタの先頭には、書かなくても super(); (親クラスの引数なしコンストラクタの呼び出し) が自動で入ります。そのため、オブジェクトを作ると親クラスのコンストラクタが先に実行されます。
class Vehicle { Vehicle() { System.out.println("Vehicle"); }}class Car extends Vehicle { Car() { System.out.println("Car"); }}public class Main { public static void main(String[] args) { new Car(); }}java Main Vehicle Car
親クラスに引数なしのコンストラクタがない場合 (引数ありのコンストラクタだけを定義した場合) は、自動で入る super(); が呼び出し先を見つけられず、コンパイルエラーになります。サブクラスのコンストラクタの先頭で super("名前") のように、引数を渡して明示的に呼び出す必要があります。
class Parent { Parent(String name) { System.out.println(name); }}class Child extends Parent { Child() { System.out.println("Child"); }}public class Main { public static void main(String[] args) { new Child(); }}javac Main.java
コンパイルエラー (8 行目): Parent に引数なしのコンストラクタがないため、暗黙の super() を呼び出せない
super(...) も this(...) と同じく、コンストラクタの最初の文に書かなければなりません。
メソッドのオーバーライド
親クラスのメソッドと同じ名前・同じ引数 (型・数・順番) のメソッドをサブクラスで定義すると、親のメソッドを上書きするオーバーライドになります。戻り値の型も基本的に同じにします (int を long に変えるなどはコンパイルエラー)。メソッドの前に @Override を付けると、正しくオーバーライドできていない場合にコンパイラがエラーで知らせてくれます。
サブクラスのメソッドの中で super.メソッド名() と書くと、オーバーライドする前の親クラスのメソッドを呼び出せます。
class Animal { void speak() { System.out.println("..."); }}class Dog extends Animal { @Override void speak() { System.out.println("ワン"); super.speak(); }}public class Main { public static void main(String[] args) { Animal a = new Dog(); a.speak(); }}java Main ワン ...
オーバーライドするメソッドのアクセス修飾子は、元のメソッドと同じか、より広くしなければなりません。public のメソッドを修飾子なし (パッケージ内だけに公開) でオーバーライドすると、範囲が狭くなるのでコンパイルエラーです。
class Parent { public void show() { System.out.println("Parent"); }}class Child extends Parent { void show() { System.out.println("Child"); }}public class Main { public static void main(String[] args) { new Child().show(); }}javac Main.java
コンパイルエラー (8 行目): public の show() を、それより狭いアクセス範囲でオーバーライドしている
引数が違う同名のメソッドは、オーバーライドではなくオーバーロード (別のメソッドの追加) になります。また、親クラスの private メソッドはサブクラスに引き継がれないので、同じ名前のメソッドを書いてもオーバーライドにはなりません。
ポリモーフィズムと「変数の型」
親クラスの型の変数には、サブクラスのオブジェクトを代入できます (Animal a = new Dog();)。このとき、
・呼び出せるメソッドは、変数の型 (Animal) に定義されているものだけ
・実際に実行される処理は、オブジェクトのクラス (Dog) でオーバーライドされたもの
となります。サブクラスにだけあるメソッドは、親クラスの型の変数からは呼び出せません。実体がそのクラスのオブジェクトでも、コンパイルエラーです。
class Parent { void hello() { System.out.println("hello"); }}class Child extends Parent { void bye() { System.out.println("bye"); }}public class Main { public static void main(String[] args) { Parent p = new Child(); p.hello(); p.bye(); }}javac Main.java
コンパイルエラー (17 行目): 変数 p の型 Parent には bye() が定義されていない
抽象クラスを使うと、共通の処理は親クラスに書き、違う部分だけをサブクラスに実装させられます。親クラスのメソッドの中から呼んだ抽象メソッドも、実行時のオブジェクトのクラスの実装が使われます。
abstract class Employee { String name; Employee(String name) { this.name = name; } abstract int salary(); void print() { System.out.println(name + ": " + salary()); }}class Engineer extends Employee { Engineer(String name) { super(name); } int salary() { return 300; }}class Manager extends Employee { Manager(String name) { super(name); } int salary() { return 450; }}public class Main { public static void main(String[] args) { Employee[] staff = {new Engineer("佐藤"), new Manager("鈴木")}; int total = 0; for (Employee e : staff) { e.print(); total += e.salary(); } System.out.println("合計: " + total); }}java Main 佐藤: 300 鈴木: 450 合計: 750
参照型の型変換 (キャスト) と instanceof
サブクラスの型から親クラスの型への変換 (アップキャスト) は、キャストを書かなくても自動で行われます。逆に、親クラスの型からサブクラスの型への変換 (ダウンキャスト) は、(Dog) a のようにキャストを明示する必要があります。
ダウンキャストは、実体が本当にそのクラスのオブジェクトなら成功します。実体が違うクラス (例えば Cat) だった場合、コンパイルは通りますが、実行時に例外 (ClassCastException) が発生します。キャストの前に instanceof で確かめると安全です。
class Animal {}class Dog extends Animal { void bark() { System.out.println("ワン"); }}class Cat extends Animal {}public class Main { public static void main(String[] args) { Animal[] animals = {new Dog(), new Cat()}; for (Animal a : animals) { if (a instanceof Dog) { Dog d = (Dog) a; d.bark(); } else { System.out.println("Dog ではない"); } } Dog d = (Dog) animals[1]; }}java Main ワン Dog ではない
実行時に例外が発生: ClassCastException
最後の行の (Dog) animals[1] は、実体が Cat なので実行時に例外になります。一方、Dog と String のように継承関係がまったくない型どうしのキャストは、成功する可能性がないのでコンパイルエラーになります。
パッケージ宣言とインポート
ソースファイルの中の順番は、package 宣言 → import 文 → クラスの宣言と決まっています。package 宣言はファイルの先頭 (コメントは除く) に 1 つだけ書けます。
package com.example.shop;import java.util.ArrayList;import java.util.List;public class Cart { private List<String> items = new ArrayList<>();}次のクラスは、import を書かなくても使えます。
・java.lang パッケージのクラス (String・Math・System・Integer など)
・同じパッケージのクラス
import java.util.*; のように * を使うと、そのパッケージ直下のクラスをまとめてインポートできます。ただし、サブパッケージのクラスは含まれません (java.util.* で java.util.function のクラスは使えない)。import を書かずに java.util.List のように完全修飾名で書くこともできます。
試験でよくある引っかけ
暗黙の
super()と親のコンストラクタ親クラスに引数なしのコンストラクタがないと、サブクラスのコンストラクタで
super(...)を明示しない限りコンパイルエラーです。オーバーライドでアクセスを狭める
publicのメソッドを、修飾子なしやprivateでオーバーライドするとコンパイルエラーです。親の型の変数からサブクラスのメソッド
Parent p = new Child();のpからは、Childにしかないメソッドを呼べません (コンパイルエラー)。ダウンキャストの失敗
実体が違うクラスのダウンキャストは、コンパイルは通り、実行時に
ClassCastExceptionになります。引数が違う同名メソッド
引数の型や数が違うと、オーバーライドではなくオーバーロードです。親の型の変数からは呼べません。
packageとimportの順番package宣言 →import文 → クラスの順です。importをpackageより前に書くとコンパイルエラーです。
問題で確認する
この分野の問題を、解説つきで読めます (登録不要)。
出典・参考
- Oracle University 日本「1Z0-818-JPN (Java SE Bronze) 試験」
- Java 言語仕様 (Java SE 11) 8.4.8「Inheritance, Overriding, and Hiding」
- Java 言語仕様 (Java SE 11) 5.5「Casting Contexts」
- Java 言語仕様 (Java SE 11) 7「Packages and Modules」
コード例は、試験の対象バージョンで実際にコンパイル・実行して結果を確認しています。