継承とインタフェースの使用の要点
内容の確認日: 2026-10-04
この分野は Silver で最も出題項目が多く、継承の細かいルールと、Java SE 8 以降に拡張されたインタフェース (デフォルト・メソッド、static メソッド、プライベート・メソッド)、Java SE 17 で正式に入ったシール・クラスが問われます。
「オーバーライドされるのはインスタンスメソッドだけ」「どのメソッドを呼ぶかの決まり方は、オーバーロードはコンパイル時、オーバーライドは実行時」という 2 つの原則を押さえると、紛らわしい問題も整理できます。
コンストラクタの連鎖
コンストラクタの最初の文は、this(...) (同じクラスの別のコンストラクタ) か super(...) (親クラスのコンストラクタ) のどちらかです。どちらも書かなければ super(); が自動で入ります。そのため new すると、親クラスのコンストラクタの本体から順に実行されます。
class Base { Base(int level) { System.out.println("Base(" + level + ")"); }}class Derived extends Base { Derived() { this(1); System.out.println("Derived()"); } Derived(int level) { super(level * 10); System.out.println("Derived(int)"); }}public class Main { public static void main(String[] args) { new Derived(); }}java Main Base(10) Derived(int) Derived()
コンストラクタを 1 つも書かないサブクラスには Sub() { super(); } というデフォルトコンストラクタが追加されます。親クラスに引数なしのコンストラクタがなければ、このときコンパイルエラーになります。
オーバーライドされるもの・されないもの
オーバーライドされるのはインスタンスメソッドだけです。フィールドと static メソッドはオーバーライドされず、サブクラスの同名のものは親のものを隠すだけです。隠されたフィールドや static メソッドのどちらが使われるかは、変数の型で決まります。
class Parent { String name = "Parent"; String getName() { return name; } static String kind() { return "Parent.kind"; }}class Child extends Parent { String name = "Child"; @Override String getName() { return name; } static String kind() { return "Child.kind"; }}public class Main { public static void main(String[] args) { Parent p = new Child(); System.out.println(p.name); System.out.println(p.getName()); System.out.println(p.kind()); }}java Main Parent Child Parent.kind
オーバーライドするメソッドには、次の制限があります。
・アクセス修飾子は、元と同じかより広く (protected → public は可、逆は不可)
・戻り値の型は、元と同じかそのサブクラス (共変戻り値。プリミティブ型は同じ型のみ)
・throws に書けるチェック例外は、元と同じかそのサブクラスだけ (減らしたり、なくしたりするのは可)
・final メソッドはオーバーライドできない
・インスタンスメソッドを static メソッドで隠す (またはその逆) ことはできない
import java.io.IOException;class Loader { void load() throws IOException { }}class FileLoader extends Loader { @Override void load() throws Exception { }}public class Main { public static void main(String[] args) { System.out.println("ok"); }}javac Main.java
コンパイルエラー (10 行目): 元のメソッドの IOException より広い Exception を throws に書いている
キャストと、呼ばれるメソッドの決まり方
クラス型どうしのキャストは、継承関係にある場合だけ書けます。継承関係のないクラスへのキャストはコンパイルエラーです。一方、final でないクラスからインタフェースへのキャストは、そのクラス自身がインタフェースを実装していなくてもコンパイルは通ります (サブクラスが実装しているかもしれないため)。実体が実装していなければ、実行時に ClassCastException になります。
interface Flyer {}class Bird {}public class Main { public static void main(String[] args) { Bird b = new Bird(); System.out.println("キャスト前"); Flyer f = (Flyer) b; System.out.println("キャスト後"); }}java Main キャスト前
実行時に例外が発生: ClassCastException
オーバーライドされたメソッドは、実行時のオブジェクトのクラスのものが呼ばれます。一方、オーバーロードのどれを呼ぶかは、コンパイル時に、引数の変数の型で決まります。実体が String でも、Object 型の変数を渡せば Object 版が呼ばれます。
class Printer { void print(Object o) { System.out.println("Object"); } void print(String s) { System.out.println("String"); }}public class Main { public static void main(String[] args) { Printer p = new Printer(); Object o = "text"; p.print(o); p.print("text"); }}java Main Object String
抽象クラスと final クラス
抽象クラスは new できませんが、コンストラクタを持てます (サブクラスの super(...) から呼ばれます)。また、インタフェースを実装した抽象クラスは、インタフェースのメソッドの一部を実装しないままにできます。残りは具象サブクラスで実装します。
final を付けたクラスは継承できず、final を付けたメソッドはオーバーライドできません。String・Integer などの多くの標準クラスは final です。abstract と final は意味が矛盾するので、同時には付けられません。
インタフェースのメソッド
インタフェースには、抽象メソッドのほかに次のメソッドを書けます。
・デフォルト・メソッド (Java SE 8): default を付けた、処理を持つメソッド。実装クラスに引き継がれ、オーバーライドもできる
・static メソッド (Java SE 8): 実装クラスには引き継がれない。インタフェース名.メソッド名() でしか呼べない
・プライベート・メソッド (Java SE 9): インタフェースの中のデフォルト・メソッドなどから共通処理として呼ぶためのメソッド
interface Calculator { static int twice(int n) { return n * 2; } default int addTax(int price) { return round(price * 1.1); } private int round(double value) { return (int) Math.round(value); }}class Shop implements Calculator {}public class Main { public static void main(String[] args) { System.out.println(Calculator.twice(21)); System.out.println(new Shop().addTax(1000)); }}java Main 42 1100
Shop.twice(21) や new Shop().twice(21) と書くと、static メソッドは引き継がれていないのでコンパイルエラーです。
同じシグネチャのデフォルト・メソッドを持つ 2 つのインタフェースを実装すると、どちらを使うか決まらないので、クラスでオーバーライドしなければコンパイルエラーです。オーバーライドの中では インタフェース名.super.メソッド名() で、それぞれのデフォルト・メソッドを呼べます。また、親クラスから継承したメソッドとデフォルト・メソッドが衝突した場合は、親クラスのメソッドが優先されます。
interface Walker { default String move() { return "歩く"; }}interface Swimmer { default String move() { return "泳ぐ"; }}class Duck implements Walker, Swimmer { @Override public String move() { return Walker.super.move() + "と" + Swimmer.super.move(); }}class Animal { public String move() { return "Animal の move"; }}class Frog extends Animal implements Walker {}public class Main { public static void main(String[] args) { System.out.println(new Duck().move()); System.out.println(new Frog().move()); }}java Main 歩くと泳ぐ Animal の move
シール・クラス
Java SE 17 からのシール・クラス (sealed) は、継承・実装できる型を permits に書いたものだけに限定する仕組みです。permits に書かれていないクラスが継承しようとすると、コンパイルエラーになります。
sealed interface Shape permits Circle {}final class Circle implements Shape {}final class Square implements Shape {}public class Main { public static void main(String[] args) { System.out.println("ok"); }}javac Main.java
コンパイルエラー (7 行目): Square は Shape の permits に含まれていない
permits に書いたサブクラスには、次のどれかを必ず付けます。付け忘れるとコンパイルエラーです。
・final: これ以上継承させない
・sealed: さらに継承先を permits で限定する
・non-sealed: 制限を解除し、どのクラスでも継承できるようにする
サブクラスがすべて同じソースファイルにある場合は、permits を省略できます (そのファイルのサブクラスが許可されたものになります)。
sealed abstract class Ticket { abstract int price();}final class Adult extends Ticket { int price() { return 1800; }}non-sealed class Child extends Ticket { int price() { return 1000; }}class Infant extends Child { @Override int price() { return 0; }}public class Main { public static void main(String[] args) { Ticket[] tickets = {new Adult(), new Child(), new Infant()}; int total = 0; for (Ticket t : tickets) { total += t.price(); } System.out.println(total); }}java Main 2800
Child は non-sealed なので、permits に関係なく Infant が継承できます。
試験でよくある引っかけ
フィールドと
staticメソッドは隠すだけ親の型の変数からは、親のフィールド・
staticメソッドが使われます。オーバーライドされるのはインスタンスメソッドだけです。オーバーライドの
throws元のメソッドより広いチェック例外を
throwsに書くとコンパイルエラーです。減らすのは問題ありません。インタフェースへのキャスト
finalでないクラスからインタフェースへのキャストはコンパイルが通り、実体が実装していなければ実行時にClassCastExceptionです。オーバーロードは変数の型で決まる
Object o = "text"; p.print(o);はprint(Object)が呼ばれます。オーバーロードはコンパイル時に決まります。インタフェースの
staticメソッド実装クラスには引き継がれません。
インタフェース名.メソッド名()で呼びます。permitsのサブクラスの修飾子シール・クラスの許可されたサブクラスには、
final・sealed・non-sealedのどれかが必要です。
問題で確認する
この分野の問題を、解説つきで読めます (登録不要)。
出典・参考
- Oracle University 日本「Java SE 17 Programmer I (1Z0-825-JPN) 試験」
- JEP 409: Sealed Classes
- Java 言語仕様 (Java SE 17) 8.4.8「Inheritance, Overriding, and Hiding」
- Java 言語仕様 (Java SE 17) 9.4「Method Declarations」 (インタフェース)
コード例は、試験の対象バージョンで実際にコンパイル・実行して結果を確認しています。