そこに仁義はあるのか(仮)

略してそこ仁!

さくらばさんにJava8を教えてもらえる贅沢イベントに行ってきたよ! #javajo

【東京】◆Java SE 8勉強会◆女子部もラムダ式に挑戦! - Java女子部 | Doorkeeper
に参加してきました!

遅刻してしまって、前半の『Java SE 8の基礎解説』には出れなかったんだけど、
後半の『ラムダ式ハンズオン』で前半の『Java SE 8の基礎解説』で勉強した内容を試してみよう!
っていう感じだったので十分楽しめました!

参加者のみなさんは、(会場で手を挙げてアンケートとってたけど、)GitHub使った事ない人がほとんどで、
GitHubに自分のリポジトリありますかって質問に2人しか手が挙がらない感じだった。
でも、GitHubからPJをもってくるところとか、PJをIDEで開くところとかを、
運営の人が丁寧に教えてあげてて、初心者歓迎の言葉にうそはなかったんだ!と思った!*1

1時間半のハンズオンってことだったので、内容は絞ってたけど、
それでも、難しそう!と思ってたラムダ式、Streamに親しみをもてたので、
すごくいい感じだった!!
さくらばさん!運営の方々、みなさまみなさま、ありがとうございました!

勉強資料はこちら

skrb/LambdaOkeiko · GitHub

勉強したこと

※内容に誤りがあったら教えてください><

ラムダ式を使えるとき

実装しなければいけないメソッドが一つのインターフェースを使うときに、ラムダ式を使う事が出来る

ラムダ式を使うことによって、実際の処理とは関係ない部分をはぶく事が出来る!

基本の構文

(引数) -> {
    実際の処理
}
例)
  • 引数が0個のとき
() -> {
    実際の処理
}

  • 引数が1個のとき

※引数の()を省略することが出来る

x -> {
    実際の処理
}
(x, y) -> {
    実際の処理
}
  • 実際の処理が一文のとき

※{}を書かないで済む!『->』の前で改行してもOK

(引数) -> 実際の処理

ラムダ式の例

※実際に教材として使ったコードです

まずは、ふつうのコード

Function<String, Integer> function = new Function<String, Integer>() {
    @Override
    public Integer apply(String x) {
        return Integer.valueOf(x);
    }
};

Function<T,R>は実装する必要のあるメソッドがapply(T t)のみ。
Function<T,R>のRとTは↓こういう意味

  • T - 関数の入力の型
  • R - 関数の結果の型

つまり、

  • new Function<String, Integer>() ⇒ functionの宣言を見ればわかる。不要。
  • @Override ⇒ 本質とは関係ない。不要。
  • public Integer ⇒ functionの宣言を見ればわかる。不要。
  • apply ⇒ Functionで実装するメソッドは一つ。特定可能。不要。
  • (String ⇒ functionの宣言を見ればわかる。不要。

本質の部分は『xという引数をメソッド内でどのように使うか。』のところ。
ラムダ式を使うことで、

Function<String, Integer> function = x -> Integer.valueOf(x);

こんな感じで不要な部分をすべて除いて書くことができる*2

Stream API

Stream APIメソッドは、中間操作をするものと終端操作をするものがある

今回勉強したのは↓このメソッド

中間操作
  • filter
  • map
  • flatMap
終端操作
  • forEach
  • collect

使い方

終端操作を先に書いてから、中間操作として切り出せるものを切り出していく

終端操作のforEachよりも中間操作のfilterを先に使うのは、
filterで要素を事前に絞り込むことで、forEachで繰り返し処理として使用する要素を少なくすることが出来て、
処理を効率化することができる!からです。

Streamの例

private void printList(List<String> texts) {
    for (String text: texts) {
        if (text.startsWith("a")) {
            System.out.println(text);
        }
    }
}

まずは、textsをStreamにして、終端操作のforEachをつかう

private void printList(List<String> texts) {
    texts.stream()
        .forEach(text -> {
            if (text.startsWith("a")) {
                System.out.println(text);
            }
        });
}

ifの部分はfilterとして切り出すことができる!

private void printList(List<String> texts) {
    texts.stream()
        .filter(text -> text.startWith("a"))
        .forEach(text -> {
            System.out.println(text);
        });
}

Streamを使うことで処理がわかりやすくなる!やったね₍₍⁽⁽(ી(´・ᴗ・`)ʃ)₎₎⁾⁾

終端操作のcollectメソッド

終端操作にcollectメソッドを使うと、streamの要素を集約することができる!

例えば

toList()メソッドで、streamをListに変換することができる

private List<String> copyList(List<String> src) {
    List<String> dest = new ArrayList<>();
    
    for (String element: src) {
        dest.add(element);
    }
    
    return dest;
}

forEachを使うと、こうなる

private List<String> copyList1(List<String> src) {
    List<String> dest = new ArrayList<>();

    src.forEach(element -> {
        dest.add(element);
    });

    return dest;
}

ただし、この状態ではラムダ式のなかでdestっていうラムダ式の外で宣言した変数を使ってしまっている!
これだと、スレッドセーフにならない!
基本的に、匿名クラスやラムダ式を使うときは中に閉じた実装にするべき!

ここで、collectメソッドを使って、streamをListにしちゃって、そのままdestに代入しちゃう!
collect()メソッドにCollectors.toList()を渡す事で、streamをListに変換することができる!

private List<String> copyList2(List<String> src) {
    List<String> dest = src.stream()
            .collect(Collectors.toList());

    return dest;
}

これで、見た目にもわかりやすい実装が出来る!!*3

Java8たのしい!

ほかにも、短い時間だったけど、いろいろなこと教えてもらいました!!

  • IntStream
  • map()
  • mapToObj()
  • groupingBy()
  • flatMap()

などなど。。。

Java8たのしい!

*1:初心者向けって書いてあるのに初心者向けじゃない!みたいなの、たまーにありますよね…。

*2:『Function<String, Integer> function = Integer::valueOf;』こういう風に書けるっていうのは、今回のハンズオンではやりませんでした。GitHub上の解答には記載されてる!例えばコレのtestLambda4メソッド

*3:今回は練習用ソースだったので、Listの値をコピーする、っていう処理を実装をしました!本来はArrays.copy()を使う!