Edited at

Java8で難解コーディング 良いのか悪いのかわからない活用法

More than 3 years have passed since last update.

Java8の良いのか悪いのかわからない活用法を紹介します。

若干難解なので職場で活用して先輩に怒られても知りません。


AutoCloseableってもしかして関数型インターフェース!??

AutoCloseableは@FunctionalInterfaceはもちろんついていませんが、

関数型インターフェースの定義からすれば関数型インターフェースですね。

つまり


AutoCloseableでないクラスをtry-with-resourcesでクローズする

Graphicsなんかまさにdisposeめんどくさいですね。

    /**

* 自力でGraphicsをclose
*/

public static void disposeSample(BufferedImage image) {
Graphics graphics = image.createGraphics();
try {
//処理
} finally {
graphics.dispose();
}
}

簡単にはこうですが、場合によってはgraphicsのnull判定入れたりめんどくさいことこの上ないです。

ですがメソッド参照で

    /**

* ラムダ式でAutoCloseable化。try-with-resourcesに。
*/

public static void disposeSample(BufferedImage image) {
Graphics graphics = image.createGraphics();
try (AutoCloseable closeable = graphics::dispose) {
//処理
} catch (Exception e) {
e.printStackTrace();
}
}

とするとtry-with-resourcesが使えるわけです

参考:AutoCloseableでなくてもtry-with-resourcesがしたい

が、これだとExceptionをcatchしないといけないので

    /**

* throwsなしのAutoCloseable
*/

public interface MyCloseable extends AutoCloseable {
@Override
void close();
}

/**
* catchが不要になった
*/

public static void disposeSample(BufferedImage image) {
Graphics graphics = image.createGraphics();
try (MyCloseable closeable = graphics::dispose) {
//処理
}
}

とすれば完璧です!!MyCloseableを一つ作っておきましょう!

もう一段階難解にしたいなら下記のようにするのもおすすめです。

    public static void disposeSample(BufferedImage image) {

Graphics graphics;
try (MyCloseable closeable = (graphics = image.createGraphics())::dispose) {
//処理
}
}


Iterableってもしかして関数型インターフェース!??

Iterableは@FunctionalInterfaceはもちろんついていませんが、

関数型インターフェースの定義からすれば関数型インターフェースですね。

つまり、


Iteratorを拡張for文で回す

    /**

* Iteratorを拡張for文で回す。
*/

public static void iteratorFor() {
Iterator<String> iterator = Arrays.asList("A", "B", "C").iterator();//Iteratorを作りたいだけ。
for (String s : (Iterable<String>) () -> iterator) {
System.out.println(s);
}
}

とすると、Iteratorで拡張for文が使えるわけです!

もちろん今回の場合、普通はIterator#forEachRemainingを使って

    /**

* 普通にIterator#forEachRemaining
*/

public static void iteratorFor() {
Iterator<String> iterator = Arrays.asList("A", "B", "C").iterator();//Iteratorを作りたいだけ。
iterator.forEachRemaining(System.out::println);
}

とJavaさんからは推奨されそうですが、

この場合、breakできないのが玉に傷ですね。

    /**

* Iteratorを拡張for文で回す。
*/

public static void iteratorFor() {
Iterator<String> iterator = Arrays.asList("A", "B", "C").iterator();//Iteratorを作りたいだけ。
for (String s : (Iterable<String>) () -> iterator) {
if(s.equals("B"))
break;
System.out.println(s);
}
}

とかね。


Listライクな情報からStream生成

これは難解コーディングではありませんが、次のテーマの下準備です。

sizeっぽい情報と、get(index)っぽいメソッドがあればStreamにすることができます。

    /**

* StringBuilderをCharacterのListにする<br>
* 途中CharacterのStreamにしてる
*/

public static List<Character> stringBuilderToCharList() {
StringBuilder sb = new StringBuilder("ABC");
return IntStream.range(0, sb.length())
.mapToObj(sb::charAt)
.collect(Collectors.toList());
}

参考:http://www.ne.jp/asahi/hishidama/home/tech/java/stream.html#h_outline

の例「Excelのシート一覧取得」

さあここから、


Streamを拡張for文で回す(Listライクな情報を拡張for文で回す)

Streamからiterator()を呼び出してラムダ式でIterable化すれば拡張for文が使えます。

    /**

* StringBuilderを拡張for文で回す。
*/

public static void stringBuilderFor() {
StringBuilder sb = new StringBuilder("ABC");
for (Character c : (Iterable<Character>) () -> IntStream.range(0, sb.length()).mapToObj(sb::charAt)
.iterator()) {
System.out.println(c);
}
}

もちろんStreamもforEachメソッド持ってるので通常はそちらを使うべきですが、bre(略)

でもでも、collect(Collectors.toList())したら普通に拡張for文使えますよね?

その通り。使えますが比較してみると大きな違いがあります。


準備

    private static class ListLike {

public int size() {
return 10;
}

public String get(int index) {
System.out.println("get(" + index + ")");
return Integer.toString(index);
}
}



iteratorから

    public static void iteratorFor() {

System.out.println("iteratorFor");
ListLike list = new ListLike();
for (String s : (Iterable<String>) () -> IntStream.range(0, list.size())
.mapToObj(list::get)
.iterator()) {
if(s.equals("4")){
break;
}
System.out.println("each " + s);
}
}


collectから

    public static void collectFor() {

System.out.println("collectFor");
ListLike list = new ListLike();
for (String s : IntStream.range(0, list.size())
.mapToObj(list::get)
.collect(Collectors.toList())) {
if(s.equals("4")){
break;
}
System.out.println("each " + s);
}
}

実行結果はそれぞれ


  • iteratorFor


iteratorFor

get(0)

each 0

get(1)

each 1

get(2)

each 2

get(3)

each 3

get(4)



  • collectFor


collectFor

get(0)

get(1)

get(2)

get(3)

get(4)

get(5)

get(6)

get(7)

get(8)

get(9)

each 0

each 1

each 2

each 3


となりcollectは一回リストにするのでその分無駄があります。

やはりIterableをラムダ式で生成するのがスマート(!?)ですね!


こちらもどうぞ

Java8が好きになる話(StreamAPIの話はしない) その1 Map#computeIfAbsentとList#sort

Java8が好きになる話(StreamAPIの話はしない) その2 Optional#map

Java8が好きになる話(StreamAPIの話はしない) その3 インターフェースのデフォルト実装

Java8が好きになる話(StreamAPIの話はしない) その4 ラムダ式のインスタンス