【Java8】StreamとかFunctionとかを写経してみる

Java8から導入されるjava.util.streamjava.util.functionの使用例を写経します。例題としてはこの辺のを使い回します。

後、何で当時気づかなかったのかわからないんですが、配列をListにしたかったらArrays#asListで十分でしたね。アホですね。

Streamを使ってStringの配列をIntegerのリストにする

ちょっと例題を書き換え。try-catchよりは正規表現の方がまだスマートな気がするので…。

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String[] ids = req.getParameter("IDs").split(";");
    
    List<Integer> idList = new ArrayList<Integer>();
    Pattern p = Pattern.compile("[^0-9]");

    for(String id : ids) {
        
        if(p.matcher(id).find()) continue;
        
        idList.add(Integer.parseInt(id));
    }

}

Streamを使ってみましょう。

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    Pattern p = Pattern.compile("[^0-9]");
    
    List<Integer> idList = req.getParameter("IDs")
                              .split(";")
                              .stream()
                              .filter(id -> !p.matcher(id).find())
                              .mapToInt(Integer::parseInt)
                              .collect(Collectors.toList());
}

このCollectorsはなんとかならなかったんですかね?Streamのドキュメントだけ眺めれば良いようにして欲しかった…。

それと、どうしてもプリミティブ型の制約があるせいか、mapToIntとかToIntFunctionとかIntStreamみたいなものが生まれてしまうのが非常に残念です。IDEを使っていたらそれほど気にはならないんでしょうが…。

とは言え、大分書きやすくなりました。何よりローカル変数にfinalをつけなくても事実上のfinalなら無名クラスでも参照できるのが本当にありがたいです。最近ひたすら擬似クロージャを使っていたのでずっとイライラしていました。

Functionも写経してみる

java.util.functionがあればAndroidの開発がもっと楽になるのになー、とか、そんなことを思うんですが、未だにJava7にも正式対応してないので、いっそJava捨ててくれればなーと思っています。

さて、以前作ったClickableItem<EventArg>を書き直してみます。

public class ClickableItem<EventArg> implements Serialize {

    private static final long serialVersionUID = -1L;
    private String name;
    private Consumer<EventArg> event;
    
    public ClickableItem(String name, Consumer<EventArg> event) {
        this.name = name;
        this.event = event;
    }
    
    //各アクセサは面倒なので割愛
    
    public void invoke(EventArg arg) {
        this.event.accept(arg);
    }
    
    @Override
    public String toString() {
        return this.name;
    }
    
}

写経ってほどでもないですね。てかConsumerってクラス名はどうなの…?

実際の使用例も書き換えてみましょう。

public class TestActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ArrayAdapter<ClickableItem<String>> adapter = new ArrayAdapter<>(getApplicationContext()
                        , android.R.layout.simple_list_item_1);

        adapter.add(new ClickableItem<>("hoge", s -> {
            //TODO:hoge
        }));
        
        adapter.add(new ClickableItem<>("fuga", s -> {
            //TODO:fuga
        }));
        
        adapter.add(new ClickableItem<>("piyo", s -> {
            //TODO:piyo
        }));
        
        ListView listView = (ListView) findViewById(id.listView);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(parent, view, position, id -> {
            ListView listView = (ListView) parent;
            ClickableItem<String> item = (ClickableItem<String>) listView.getItemAtPosition(position);
            item.invoke(item.getName());
        });
    }
}

ラムダ式が書けるようになっただけでなく、Java8からダイアモンドオペレータを使える場所が増えるおかげで記述量が圧倒的に減ります。ってかvarをよこせ。マジで。

AdapterView.OnItemClickListenerはonItemClickしかメソッドを持っていない=FunctionalInterfaceなので、私の理解が間違っていなければラムダ式が書けるはずなんですが、どうなんでしょう。

まとめ

ちょっと体調が芳しくないのでこの辺にしておきます。

java.util.functionはまだまだ面白そうなもの(合成とか継続とか)があるし、Streamも全然いじれてないので色々遊びたいんですが、実際に使えるようになるのがいつになるのかわからないので全然モチベーションがあがりません。Streamは正直java.util.functionさえあれば自分の欲しいものだけ実装できるので、そこだけでも早く欲しいですね。