Java Language
デフォルトメソッド
サーチ…
前書き
Java 8で導入されたデフォルトメソッドは、開発者がこのインタフェースの既存の実装を破ることなく新しいメソッドをインタフェースに追加することを可能にします。インタフェースを実装するクラスがそのメソッドの実装を提供できないときに、デフォルトとして使用される実装をインタフェースが定義できるようにする柔軟性を提供します。
構文
- パブリックのデフォルトvoid methodName(){/ * method body * /}
備考
デフォルトのメソッド
- 既存のサブクラスで強制的に実装することなく、動作を導入するために、インタフェース内で使用できます。
- サブクラスまたはサブインターフェースによってオーバーライドできます。
- java.lang.Objectクラスのメソッドをオーバーライドすることはできません。
- 複数のインタフェースを実装しているクラスが、それぞれのインタフェースから同じメソッドシグネチャを持つデフォルトメソッドを継承する場合は、(複数継承の解決の一環として)デフォルトメソッドでないかのように独自のインタフェースをオーバーライドして提供する必要があります。
- 既存の実装を破ることなく動作を導入することを意図していますが、新しく導入されたデフォルトメソッドと同じメソッドシグニチャーを持つ静的メソッドを持つ既存のサブクラスは依然として破損します。しかし、スーパークラスにインスタンスメソッドを導入した場合でも、これは当てはまります。
静的メソッド
- インターフェイス内で使用できます。主にデフォルトメソッドのユーティリティメソッドとして使用されます。
- サブクラスまたはサブインターフェース(それらに隠されている)によってオーバーライドすることはできません。しかし、今でも静的メソッドの場合と同様に、各クラスまたはインタフェースは独自のものを持つことができます。
- java.lang.Objectクラスのインスタンスメソッドをオーバーライドすることはできません(現時点ではサブクラスの場合も同様です)。
以下は、サブクラスとスーパークラスの相互作用を要約した表です。
- | SUPER_CLASS-INSTANCE-METHOD | スーパークラス静的メソッド |
---|---|---|
SUB_CLASS-INSTANCE-METHOD | オーバーライド | 生成 - コンパイル時エラー |
SUB_CLASS-STATIC-METHOD | 生成 - コンパイル時エラー | 隠す |
以下は、インタフェースと実装クラス間の相互作用を要約した表です。
- | インタフェース初期化メソッド | インターフェース静的メソッド |
---|---|---|
IMPL_CLASS-INSTANCE-METHOD | オーバーライド | 隠す |
IMPL_CLASS-STATIC-METHOD | 生成 - コンパイル時エラー | 隠す |
参考文献:
- http://www.journaldev.com/2752/java-8-interface-changes-static-method-default-method
- https://docs.oracle.com/javase/tutorial/java/IandI/override.html
デフォルトメソッドの基本的な使い方
/**
* Interface with default method
*/
public interface Printable {
default void printString() {
System.out.println( "default implementation" );
}
}
/**
* Class which falls back to default implementation of {@link #printString()}
*/
public class WithDefault
implements Printable
{
}
/**
* Custom implementation of {@link #printString()}
*/
public class OverrideDefault
implements Printable {
@Override
public void printString() {
System.out.println( "overridden implementation" );
}
}
次のステートメント
new WithDefault().printString();
new OverrideDefault().printString();
この出力を生成します:
default implementation
overridden implementation
デフォルトメソッド内の他のインタフェースメソッドへのアクセス
デフォルトメソッド内から他のインタフェースメソッドにアクセスすることもできます。
public interface Summable {
int getA();
int getB();
default int calculateSum() {
return getA() + getB();
}
}
public class Sum implements Summable {
@Override
public int getA() {
return 1;
}
@Override
public int getB() {
return 2;
}
}
次の文は3を出力します:
System.out.println(new Sum().calculateSum());
デフォルトのメソッドは、インターフェイスの静的メソッドと一緒に使用することもできます。
public interface Summable {
static int getA() {
return 1;
}
static int getB() {
return 2;
}
default int calculateSum() {
return getA() + getB();
}
}
public class Sum implements Summable {}
次のステートメントは、3を出力します。
System.out.println(new Sum().calculateSum());
オーバーライドされたデフォルトメソッドにクラスを実装することからアクセスする
クラスでは、 super.foo()
はスーパークラスのみを検索します。 super
インタフェースからデフォルトの実装を呼び出す場合は、インタフェース名Fooable.super.foo()
super
を修飾する必要があります。
public interface Fooable {
default int foo() {return 3;}
}
public class A extends Object implements Fooable {
@Override
public int foo() {
//return super.foo() + 1; //error: no method foo() in java.lang.Object
return Fooable.super.foo() + 1; //okay, returns 4
}
}
なぜデフォルトメソッドを使用するのですか?
簡単な答えは、既存の実装を破ることなく既存のインターフェースを進化させることができるということです。
たとえば、20年前に公開したSwim
インターフェイスがあります。
public interface Swim {
void backStroke();
}
私たちはすばらしい仕事をしてくれました。私たちのインターフェースは非常に普及しています。世界中の多くの実装があり、ソースコードを管理することはできません。
public class FooSwimmer implements Swim {
public void backStroke() {
System.out.println("Do backstroke");
}
}
20年後には、新しい機能をインターフェースに追加することに決めましたが、インターフェースが凍結されているように見えます。既存の実装を破るためです。
幸いJava 8では、 Defaultメソッドという新しい機能が導入されています。
Swim
インターフェイスに新しいメソッドを追加できるようになりました。
public interface Swim {
void backStroke();
default void sideStroke() {
System.out.println("Default sidestroke implementation. Can be overridden");
}
}
現在、インタフェースの既存の実装はすべて機能します。しかし、最も重要なのは、新しく追加されたメソッドを独自の時間に実装できることです。
この変更の最大の理由の1つ、そしてその最大の用途の1つは、Java Collectionsフレームワークです。 Oracleは、Iterableを実装した既存のコードをすべて破棄することなく、既存のIterableインタフェースにforeach
メソッドを追加できませんでした。デフォルトのメソッドを追加することで、既存のIterableの実装はデフォルトの実装を継承します。
クラス、抽象クラス、およびインターフェイスメソッドの優先順位
抽象宣言を含むクラス内の実装は、すべてのインタフェースのデフォルトよりも優先されます。
- 抽象クラスメソッドはインターフェイスデフォルトメソッドよりも優先されます。
public interface Swim {
default void backStroke() {
System.out.println("Swim.backStroke");
}
}
public abstract class AbstractSwimmer implements Swim {
public void backStroke() {
System.out.println("AbstractSwimmer.backStroke");
}
}
public class FooSwimmer extends AbstractSwimmer {
}
次のステートメント
new FooSwimmer().backStroke();
生産する
AbstractSwimmer.backStroke
- クラスメソッドはインターフェイスのデフォルトメソッドよりも優先されます
public interface Swim {
default void backStroke() {
System.out.println("Swim.backStroke");
}
}
public abstract class AbstractSwimmer implements Swim {
}
public class FooSwimmer extends AbstractSwimmer {
public void backStroke() {
System.out.println("FooSwimmer.backStroke");
}
}
次のステートメント
new FooSwimmer().backStroke();
生産する
FooSwimmer.backStroke
デフォルトメソッド多重継承の衝突
次の例を考えてみましょう。
public interface A {
default void foo() { System.out.println("A.foo"); }
}
public interface B {
default void foo() { System.out.println("B.foo"); }
}
同じシグニチャを持つdefault
メソッドfoo
を宣言する2つのインタフェースがありdefault
。
これらの両方のインタフェースを新しいインタフェースでextend
しようとextend
と、2つのインタフェースを選択する必要があります。なぜならJavaはこの衝突を明示的に解決する必要があるからです。
まず 、メソッドfoo
をabstract
と同じシグネチャで宣言すると、 A
とB
動作がオーバーライドされます。
public interface ABExtendsAbstract extends A, B {
@Override
void foo();
}
class
ABExtendsAbstract
をimplement
ABExtendsAbstract
は、 foo
実装を提供する必要があります:
public class ABExtendsAbstractImpl implements ABExtendsAbstract {
@Override
public void foo() { System.out.println("ABImpl.foo"); }
}
それとも二 、あなたは完全に新しい提供することができdefault
実装を。また、クラスの実装からオーバーライドされたデフォルトメソッドにアクセスすることによって、 A
およびB
foo
メソッドのコードを再利用することもできます 。
public interface ABExtends extends A, B {
@Override
default void foo() { System.out.println("ABExtends.foo"); }
}
class
ABExtends
をimplement
ABExtends
は、 foo
実装を提供する必要はありnot
:
public class ABExtendsImpl implements ABExtends {}