ラムダ式とStream API

最近atCoderを始めて、簡単な配列操作については書けるようになってきたので、次のステップとしてStream APIを使用したコーディングができるよう覚えたことをメモしておきます。 ラムダ式も一応の理解はありますが、実際に書くということに慣れていないので、こちらの記事ではラムダとStream APIの両方について書いていきたいと思います。

ラムダ式とは何か

端的に言うと

[引数] -> [処理]

の形で記述することで無名クラスの記述を簡略化できるようにしたものです。 以降で順を追って説明していきます。

ラムダ式登場前の話 無名クラスという考え方

java7以前では、ラムダ式で行っていることを、無名クラスというものを使用して実現していました。

interface Calculator {
    int culc(int a , int b);
}

このようなインターフェースがあった場合、実際に使用する際には、Calsuratorインターフェースを実装したクラスを作成する必要があります。

しかし、他で再利用する予定がなく、その場限りで必要であるメソッドであった場合、それだけのためにわざわざ実装クラスを作成するのは面倒です。 そういった場面で使われるのが、「無名クラス」です。

Calculator c = new Calculator() {
    pubic int culc (int a, int b ) {
        return  a + b;
    }
};

通常であれば、あらかじめ作成しておいたCalcuratorインターフェースの実装クラスを、変数cに代入する必要がありますが、 無名クラスでは変数に直接代入する形で実装クラスを作成しております。

その場限りで使用する一時的なものである場合、他で使われることがないため、実装クラスの名前が何であるかを意識する必要がなく、 calc()メソッドをオーバーライドした実装クラスの処理がどのような内容になっているかだけ分かれば良いということです。

上記のサンプルコードを、下記のように置き換えて読むと、分かりやすいかと思います。

  • new Culculation() ⇒ CalculationImplクラス(仮名。実装クラスであるということを表現しているだけで名前は何でもいいです。)
  • {} 内の記述 ⇒ CalculationImplクラスでオーバーライドしたcalc()メソッドの処理

本題のラムダへ

インターフェースの実装クラスの作成が、無名クラスという考え方により省略されただけでもかなり便利な気がしますが、 ラムダは更に簡単な記述で同様のことをやってのけます。

先ほどの無名クラス、元となるインターフェースが抽象メソッドを一つしか持たない場合、更に省略できる箇所があります。 それは、「メソッド名」と「引数の型」です。

無名クラス

Calculator c = new Calculator() {  //① 
    pubic int culc (int a, int b ) {  //② 
        return  a + b;
    }
};

① 変数cに代入されるべきはCalculatorの実装クラスであり、それ以外はない。ならばわざわざ書く必要もない ⇒ ラムダ式では省略

② メソッドが1つしかないのであれば、メソッド名や必要とされる引数を記述しなくても、どのメソッドを呼び出しているのだろう?となることがないため、わざわざ書く必要がない ⇒ ラムダ式では省略

ラムダ式

Calculator c = (a,b) -> {return a + b};

恐ろしく簡単になりましたね…!

ちなみに、抽象メソッドを一つしか持たないインターフェースのことを「関数型インターフェース」と呼びます。 ラムダ式でも、無名クラスの時と同様に実装クラスの作成が行われていますが、省略に省略を重ね、 引数を渡して、処理を行うという記述に限定されたことで、関数のような使い方ができるため、 このような呼ばれ方をしています。

Stream API

次に、Stream APIについて説明していきます。

Stream APIはIterationを拡張したAPIで、コレクションに対して行う複雑な処理を簡略化して記述することができます。

▼Stream APIの処理の流れ

  • 生成処理 データ列をStreamオブジェクトに変換

  • 中間操作 データを操作する

  • 末端操作 データを出力する

この処理の過程で、ラムダ式を使っていきます。

関数型インターフェースはわざわざ自分で作らなくても大丈夫

上述したように、ラムダ式は「関数型インターフェース」でないと使用できません。 「ラムダ式のために、関数型インターフェースをあらかじめ作らないといけないの…?」と思われるかもしれませんが、 その必要はありません。下記のような関数型インターフェースがあらかじめ用意されています。

抽象メソッド

R apply(T t)

T型変数tを引数に取り、R型に変換して返す

抽象メソッド

void accept(T t)

戻り値なし。T型の変数tを引数に取る

抽象メソッド

T get()

引数なし。T型で結果を返す

抽象メソッド

boolean test(T t)

T型の変数tを引数に取り、booleanで返す。


これらのインターフェースをラムダ式で扱うことで、 コレクションの操作を簡潔に扱うことができます。

Stream APIにはコレクションの操作を行う便利なメソッドがありますが、 それらのメソッドの中には上記のインターフェースを引数に取るものが数多く存在します。

つまり、メソッド呼び出し時の引数の中にラムダ式を記述するということになります。

長くなったので、続きは次回。