拡張メソッドについて調べてみた その1

【概要】

今回は拡張メソッドについて調べてみました。

拡張メソッドは既存のインターフェイス
拡張しやすくするために追加された機能です。

【拡張しやすいとは】

今までインターフェイスにメソッドを追加した場合は
実装したすべてのクラスにメソッドを実装する必要がありました。

拡張メソッドはインターフェイスにデフォルトの実装を持たせるものです。
その名のとおり各クラスから拡張メソッドが呼び出された場合、
デフォルト実装の処理が実行されます。

拡張メソッドはオーバーライドすることができるので、
拡張メソッドの内容を変更する場合はオーバーライドします。

ソースコード

[インターフェイス]

では実際に拡張メソッドを定義したインターフェイスを見てみます。

public interface SampleInterface {

    void executeImplements();

    default void executeDefault() {
        System.out.println("executeDefault executed!");
    }
}
[実装したクラス]

拡張メソッドを定義する場合はメソッドにdefaultをつけて、
通常のメソッドとして処理を記述します。

public class Sample01 implements SampleInterface{

    public static void main(final String[] args) {

        final Sample01 sample = new Sample01();
        sample.executeImplements();
        sample.executeDefault();
    }

    @Override
    public void executeImplements() {
        System.out.println("Sample1 implements excute.");
    }
}

public class Sample02 implements SampleInterface{

    public static void main(final String[] args) {

        final Sample02 sample = new Sample02();
        sample.executeImplements();
        sample.executeDefault();
    }

    @Override
    public void executeImplements() {
        System.out.println("Sample2 implements excute.");
    }

    @Override
    public void executeDefault() {

        System.out.println("Override default!");
    }
}

Sample01ではexecuteImplementsメソッドのみ実装しています。
Sample02ではexecuteImplementsメソッド、executeDefaultメソッドを実装しています。

[実行結果]
<<サンプル1の実行結果>>
Sample1 implements excute.
executeDefault executed!


<<サンプル2の実行結果>>
Sample2 implements excute.
Override default!

実行結果を見るとサンプル1側のexecuteDefaultメソッドは
デフォルト実装が実行されていることがわかります。

サンプル2側のexecuteDefaultメソッドは
オーバーライドしたメソッドが実行されていることがわかります。

【Abstractクラス】

Abstractクラスは通常のメソッドと
各クラスが実装する必要がある抽象メソッドを持つことができるクラスです。

インターフェイスに拡張メソッドが追加されたので、
Abstractクラスとの違いがあいまいになりそうです。

拡張メソッドを使えばAbstractクラスと同様のことができますが、
以下のような違いがあるため、単純な置き換えにはなりません。

Abstractクラスを使ったTemplateMethodパターンは
拡張メソッドに置き換えることができません。

処理の流れをつかさどるメソッドもオーバーライドできるため
TemplateMethodパターンが構築できません。

最近はAOPを使うことが多いのでTemplateMethodパターンを
Abstractクラスで実現するは少ないと思いますが。

【lambda内でのデフォルトメソッド】

前回、forEachメソッドについて書きましたが、
forEachメソッドもIterableインターフェイスに追加された拡張メソッドです。

public class Sample03 {

    public static void main(final String[] args) {
        final List<String> valueList = new ArrayList<>();
        valueList.add("one");
        valueList.add("two");
        valueList.add("three");

        final Iterable<String> ite = valueList;
        ite.forEach(System.out::println);
    }
}

Iterable#forEachで動作していることがわかります。

【まとめ】

ダックタイピング的に実行時にメソッドを追加することはできないので、
既存のインターフェイスにメソッドを追加しやすくるという
用途以外には役目なさそうです。

自分で拡張メソッドを追加する機会は少ないように思いますが、
標準APIでは使用されている箇所もあるので、
コードを読む際には知っておくべき機能かなと思います。