読者です 読者をやめる 読者になる 読者になる

Java8新機能 デフォルトメソッドと多重継承の違い

以前の記事でデフォルトメソッドについて書いた際、最後に

え、多重継承となにが違うの?
だれかおせーて orz

と書きましたが、やっぱりちょっと違うようです。

ラムダ式のFAQ(http://www.lambdafaq.org/)を見たところ、

Do default methods introduce multiple inheritance to Java?

No, because multiple inheritance is already present in Java. Multiple inheritance of interface types has been a feature of the language since its beginning. Default methods do introduce a new kind of multiple inheritance, namely multiple inheritance of behaviour. Java will still not have multiple inheritance of state, as for example C++ has.

デフォルトメソッドとは、Javaに多重継承を取り入れるということですか?

いいえ。なぜなら、多重継承は既にJavaに存在しているからです。Interfaceの多重継承は、最初から言語の特徴となっています。デフォルトメソッドは、新しい種類の多重継承を取り入れるということです。すなわち、"振る舞い"の多重継承です。Javaは未だに、C++のような"状態"の多重継承を持っていません。

ということらしいです。よく分からないです orz
ここでは

  • Java = "振る舞い"(behaviour)の多重継承
  • C++ = "状態"(state)の多重継承

となっているので、この違いがいったいなんだろうということになります。

ここからはあくまで個人の推測ですが。。。

Interfaceでは出来ないこと

private変数・メソッドの定義

Interfaceでは、privateの変数やメソッドの定義が出来ません。

public interface TestInterface {
    private String value = "test_value";
    private void method1();
    default private void method2() { System.out.println("method2"); }
}

これをコンパイルすると

TestInterface.java:2: エラー: 修飾子privateをここで使用することはできません
        private String value = "test_value";
                       ^
TestInterface.java:3: エラー: 修飾子privateをここで使用することはできません
        private void method1();
                     ^
TestInterface.java:4: エラー: 修飾子privateをここで使用することはできません
        default private void method2() { System.out.println("method2"); }
                             ^

と全部エラーとなります。
ちなみにprivateをpublicにすればコンパイルは通ります。

インスタンス変数の定義

Interfaceで定義できる変数はstaticのしかもfinalのみで、インスタンス変数を定義することが出来ません。
つまり

public interface TestInterface {
    public String value = "test_value";
}

public interface TestInterface {
    public static final String value = "test_value";
}

は同じことになります。
後者のコードをEclipseでチェックをかけると、staticとfinalは不要だから削除するように警告されることもあります。

デフォルトメソッドでは出来ないこと

つまり、デフォルトメソッドでは処理結果の状態を保持することが出来ないということです。
たとえばgetterやsetterを作りたい場合、

public interface TestInterface {
    private String value = "test_value";
    default public String getValue() { return value; }
    default public void setValue(String value) { this.value = value; }
}

と書きたいわけですが、

  • private使えない
  • staticだからインスタンス毎に値を保持できない
  • finalだからsetterで値の書き換えできない

ということで、getter以外はコンパイルエラーとなります。

つまり

「デフォルトメソッドでは状態を保持できないため、そのメソッド内で完結した処理しか書けない」
ということだと思います。
メソッド内で完結した処理を、そのメソッドがどう振る舞うべきかということを(簡単に)記述しているものだと考えたら、

  • Java = "振る舞い"(behaviour)の多重継承
  • C++ = "状態"(state)の多重継承

の違いになるのかなぁと。

次回予告

そもそもラムダ式のFAQなのに、なんでデフォルトメソッドについて書いてあるんだろうと思うかもしれません。

それはデフォルトメソッドがJavaに追加された経緯にラムダ式が関係しているからだと思います。
というか、ラムダ式を実現するためにはデフォルトメソッドが必要だったからなのですが。
FAQを読んで頂ければ書いてありますが、もう昼休みが終わるのでまた次回でw