日々常々

ふつうのプログラマがあたりまえにしたいこと。

DiamondProblemの対応で気になったこと

去る2014/7/12は関西Javaエンジニアの会スペシャル! Java 8リリースでした。JavaSE8に関するアレコレでぎっしり、実に充実した一日でした。

さて、その中でインタフェースにdefaultメソッドが定義できるようになった結果、起こるのじゃないかと危惧されたダイアモンド継承問題。 @bitter_fox さんのセッションではそれについても触れておりまして、Java8がどうしてるかも示されていました。

だいたいこの辺から → Brand new Data Processing - StreamAPI (P108)

詳細はスライドを見て頂くとして、ようするに "Class always win" と "インタフェース同士で衝突した場合はコンパイルエラー" だと言うこと。
となると、みんな以下の2パターンは気になるかと思います。

  • デフォルトメソッドを持つインタフェースを継承したインタフェースでデフォルトメソッドを定義した場合
  • デフォルトメソッドを持つインタフェースと持たないインタフェースを実装した後、持たない方のインタフェースがデフォルトメソッドを持ち、別にコンパイルされた場合

みんな気になると思うから試してみた。ちなみに実行環境はこれ。

% java -version
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

デフォルトメソッドの継承とオーバーライドあたり

https://gist.github.com/irof/d8362f37830f07c389f3

これで実行すると素直な感じ。インタフェース2つ入れたらなんか死ぬんじゃないか?と思ったんだけど、継承関係になってればちゃんと解決するぽい。もちろん継承してなかったら前述のコンパイルエラーだ。
とは言え継承階層が無駄に深くてIHogeとIPiyoがアレだと、IHogeのデフォルトメソッドが実行されると誤認する可能性はあるかも。そんな実装するなって話だけど、やるかもしれないね。面倒だね。上手く動くようには見えるんだけど、場合によっては気をつけなきゃいけないかも。

たとえばIPiyo extends IFuga extends IHoge となっていて、IHogeで定義されたのがIFugaで上書きされていて、IPiyoでは何もされていない場合とかだと、IFugaで上書きされたのが実行されるんだけども。まぁそんな感じ。どうでもいいか。

コンパイル後のデフォルトメソッド追加

コンパイルタイミングのチェックだとコンパイル後にコンパイルしたらアレなんじゃねーの、と。

% echo 'interface IPiyo {}' > IPiyo.java
% javac DiamondProblem.java
ls *.class
DiamondProblem.class IHoge.class IPiyo.class
% echo 'interface IPiyo { default String hoge() { return "PIYO"; } }' > IPiyo.java
% javac IPiyo.java

こんな感じ……で実行。

% java DiamondProblem 
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: IHoge.hoge IPiyo.hoge
	at DiamondProblem.hoge(DiamondProblem.java)
	at DiamondProblem.main(DiamondProblem.java:4)

エラーになった( ・ω・)b

コンパイルしようとしたら当然だめで。

% javac DiamondProblem.java 
DiamondProblem.java:1: エラー: クラス DiamondProblemは型IHogeとIPiyoからhoge()の関連しないデフォルトを継承します
public class DiamondProblem implements IHoge, IPiyo {
       ^
エラー1個

うん。満足した。
おわり。