今回もラムダ書きますけど、重要なのはそこではないので勘弁してください。
java8でインターフェースでデフォルト実装が書けるようになりました。
参考:http://www.ne.jp/asahi/hishidama/home/tech/java/interface.html#h_default_method
いつ使うの?abstractクラスの実装と何が違うの?と言われると「うっ」ってなりますが、
個人的な意見では仕様変更(機能追加)に強くなったのかと思ってます。
(関数型インターフェースでは多用しますけど今回はその話はしません。)
#例(仕様)
Containerインターフェースはkeyとuserから情報を返します。
実装クラスに下記の2種類1があります。
- 生成時に情報をロードしてキャッシュします。要求にはキャッシュから情報を返します。
- 要求に対して情報をロードして返します。一度ロードした情報はキャッシュします。
#コード
/**
* インターフェース
*/
public interface Container {
/**
* keyとuserから情報を返す
*/
String get(String key, String user);
}
public static class CacheContainer implements Container {
private final Map<String, Map<String, String>> map = new HashMap<>();
public CacheContainer() {
//TODO 初期処理でmapに情報を積む
}
@Override
public String get(String key, String user) {
return this.map.getOrDefault(key, Collections.emptyMap()).get(user);
}
}
public static class DemandContainer implements Container {
private final Map<String, Map<String, String>> map = new HashMap<>();
@Override
public String get(String key, String user) {
return this.map.computeIfAbsent(key, k -> new HashMap<>())
.computeIfAbsent(user, k -> this.getFromDB(key, user));
}
/**
* たとえばDBから値をとってきて返す。
*/
private String getFromDB(String key, String user) {
return null;//TODO DBからとってくる処理
}
}
#機能拡張(default実装なし2)
これに機能拡張が必要になって、
Containerに
String get(String key);
というメソッドを増やして、get(key,"DEFAULT");と同じ動きをさせたい。
みたいな要件来たらどうします?
/**
* インターフェース
*/
public interface Container {
/**
* keyとuserから情報を返す
*/
String get(String key, String user);
/**
* keyから情報を返す<br>
* get(key,"DEFAULT");と同様
*/
String get(String key);
}
public static class CacheContainer implements Container {
private final Map<String, Map<String, String>> map = new HashMap<>();
public CacheContainer() {
//TODO 初期処理でmapに情報を積む
}
@Override
public String get(String key, String user) {
return this.map.getOrDefault(key, Collections.emptyMap()).get(user);
}
//New!!
@Override
public String get(String key) {
return this.get(key, "DEFAULT");
}
}
public static class DemandContainer implements Container {
private final Map<String, Map<String, String>> map = new HashMap<>();
@Override
public String get(String key, String user) {
return this.map.computeIfAbsent(key, k -> new HashMap<>())
.computeIfAbsent(user, k -> this.getFromDB(key, user));
}
//New!!
@Override
public String get(String key) {
return this.get(key, "DEFAULT");
}
/**
* たとえばDBから値をとってきて返す。
*/
private String getFromDB(String key, String user) {
return null;//TODO DBからとってくる処理
}
}
あーあ。2か所に同じこと書くなんて。
abstractクラスかませておけばよかったと後悔するしかないですね。
実装クラスが2つしか無かったことが不幸中の幸いです。
#機能拡張(java8 default実装)
java8のdefault実装ならば!!!
/**
* インターフェース
*/
public interface Container {
/**
* keyとuserから情報を返す
*/
String get(String key, String user);
/**
* keyから情報を返す<br>
* get(key,"DEFAULT");と同様
*/
default String get(String key) {
return this.get(key, "DEFAULT");
}
}
とdefaultメソッド増やすだけでOK!
#おまけ
こういう感じでメソッドを増やすことができるdefaultメソッドのおかげで
java8のCollectionやMapたちも大成長を遂げたといっても過言ではありませんね。
参考:http://www.lambdafaq.org/what-are-default-methods/
(英語。僕はGoogle翻訳で読みました)
そういう意味でもデフォルト実装は大好きです。
#おまけ2
Iteratorを実装することってあります?
僕はほんとたまにあるんですけど3、removeをサポートしないことがほとんどで、
public void remove() {
throw new UnsupportedOperationException("remove");
}
って毎度書くのだるいなー。っと思ってたら、
これデフォルト実装されているので書く必要なくなりましたよ!!
その4につづく。
その1:Java8が好きになる話(StreamAPIの話はしない) その1 Map#computeIfAbsentとList#sort
その2:Java8が好きになる話(StreamAPIの話はしない) その2 Optional#map