Code Fix

上級

default メソッドの衝突と解決

複数のインターフェースから同名を継承すると衝突します。A.super.name() による明示的な選択を扱います。

エラーメッセージの読み方

Main.java:2: error: interface abstract methods cannot have body
Main.java
ファイル名
2
行番号 — javacが異常に気づいた位置。原因の行とは限りません
error
種別 — コンパイルエラー。実行まで到達しません
interface abstract methods cannot have body
内容 — javacが期待していたもの、または見つけられなかったもの

このエラーが出る典型パターン

パターン1

 1  interface Greeter {
 2               void greet() { System.out.println("hi"); }
            ^
 3  }
 4  public class Main implements Greeter {
 5      public static void main(String[] args) {
 6          new Main().greet();
 7      }
 8  }
Main.java:2: error: interface abstract methods cannot have body

インターフェースで実装を持たせるにはdefaultを付けます。付けない宣言は本体を持てません。

直し方: (空) を default にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  interface A { default String name() { return "A"; } }
 2  interface B { default String name() { return "B"; } }
 3  public class Main implements A, B {
 4                                                     
                               ^
 5      public static void main(String[] args) {
 6          System.out.println(new Main().name());
 7      }
 8  }
Main.java:3: error: class Main inherits unrelated defaults for name() from types A and B

同名のdefaultメソッドを複数継承すると衝突します。実装クラスで上書きし、A.super.name() で明示的に選びます。

直し方: (空) を public String name() { return A.super.name(); } にします。

この問題を解いてみる →

パターン3

 1  interface Greeter {
 2      private String prefix() { return "Hi, "; }
 3               void greet(String name) { System.out.println(prefix() + name); }
            ^
 4  }
 5  public class Main implements Greeter {
 6      public static void main(String[] args) {
 7          new Main().greet("Java");
 8      }
 9  }
Main.java:3: error: interface abstract methods cannot have body

インターフェースのメソッドは、本体(処理)を書くならdefaultかstaticかprivateのいずれかが必要です。何も付けなければ本体のない抽象メソッドとして扱われます。

直し方: (空) を default にします。

この問題を解いてみる →

パターン4

 1  interface MathUtils {
 2      static int square(int n) { return n * n; }
 3  }
 4  public class Main implements MathUtils {
 5      public static void main(String[] args) {
 6          System.out.println(new Main().square(5));
                                    ^
 7      }
 8  }
Main.java:5: error: cannot find symbol symbol: method square(int)

インターフェースのstaticメソッドは、defaultメソッドと違って実装クラスのインスタンスからは呼べません。インターフェース名を直接指定します。

直し方: new Main() を MathUtils にします。

この問題を解いてみる →

パターン5

 1  interface A { default String name() { return "A"; } }
 2  interface B { String name(); }
 3  public class Main implements A, B {
 4                                                     
                               ^
 5      public static void main(String[] args) {
 6          System.out.println(new Main().name());
 7      }
 8  }
Main.java:3: error: Main is not abstract and does not override abstract method name() in B

Bがname()を抽象メソッドとして再宣言しているため、Aのdefault実装は自動継承されません。実装クラス側で明示的にオーバーライドする必要があります。

直し方: (空) を public String name() { return A.super.name(); } にします。

この問題を解いてみる →

よくある誤解

defaultは既存の実装を壊さずにメソッドを追加するための仕組みです。設計の便利機能として使うと衝突します。

実務での勘所

同じシグネチャのdefaultメソッドを持つ2つのインターフェースを実装すると、Javaはどちらを優先するかを自動的には決めず、必ずコンパイルエラーにして実装クラス側に明示的な選択を強制します。これはC++の多重継承で知られる「ダイヤモンド問題」への対策です。言語が暗黙のルールで優先順位を決めてしまうと、開発者が気づかないまま意図と違う実装が使われる恐れがあります。JavaはA.super.name()のように呼び出し元を明示させることで、この種の暗黙の解決を意図的に許していません。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)