LoginSignup
51
42

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-07-15

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 ラムダ式のインスタンス

51
42
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
42