こんにちは、せきおかです。
入社1年目で日々新しいことの連続ですが、
その中でも特に面白い!と思った内容があったので自身の理解を深めることも兼ねて記事にしてみました!
今回お話する内容はずばり関数型インターフェースです!
関数型インターフェース:単一メソッドのインタフェースのことを指す
Java8から実装された新機能で、メソッドにメソッドを渡すことができる便利なやつです!
(正確には関数型インターフェースのインスタンスを渡します)
何ができるの?
- メソッドを引数に渡せる!
-
よりコンパクトなコードが書ける!
正確にはJava8が用意したインターフェースを利用して無名クラスのインスタンスを渡します。
わざわざクラスを定義してnewする手間を省けるということですね。
(しかもラムダ式という記法でさらに短い文章で書けちゃいます!) -
Stream apiの理解が深まる!
今回は話に挙げませんがStream apiでは関数型インターフェースが使われています。
本当はStream apiについても話したかったのですが長くなるので割愛…
(Stream apiを使えばfor文、if文、値の代入などなどが一行で書けちゃうんです!)
実際に見てみましょう!
やりたいこと:0~9の数字の中で、奇数と偶数を別々に出力したい!
従来の書き方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class Sample {
public static void main(String[] args) {
System.out.print("奇数:");
outputOdd();
System.out.print("\n偶数:");
outputEven();
}
static void outputOdd() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 1) {
System.out.print(i + ",");
}
}
}
static void outputEven() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
System.out.print(i + ",");
}
}
}
}
|
実行結果は以下のようになりました。
1
2
|
奇数:1,3,5,7,9,
偶数:0,2,4,6,8,
|
今度は関数型インターフェースを用いて実装してみます!
Predicateインターフェースを使った書き方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import java.util.function.Predicate;
public class Sample {
public static void main(String[] args) {
System.out.print("奇数:");
outputNum(i -> i % 2 == 1);
System.out.print("\n偶数:");
outputNum(i -> i % 2 == 0);
}
static void outputNum(Predicate<Integer> pre) {
for (int i = 0; i < 10; i++) {
if (pre.test(i)) {
System.out.print(i + ",");
}
}
}
}
|
実行結果はさきほどと同様です。
1
2
|
奇数:1,3,5,7,9,
偶数:0,2,4,6,8,
|
見慣れない記法があって困惑すると思いますが、要はbooleanを返すメソッドをmain側で書いてif文に挿入しています。
今回は奇数か偶数かを条件にしたメソッドを書いていますが、これに追加で~以上、~未満といった条件のメソッドをif文に挿入することも容易にできちゃいます。
1
|
outputNum(i -> i < 3) //出力結果:0,1,2,
|
つまり、メソッドの引数にメソッドを渡すことが可能になったわけですね!
これによって今まで仕方なく分けていたメソッドが1つにまとめられるのではないでしょうか!
解説
1
|
Predicate<Integer> pre
|
PredicateインターフェースというJava8から導入された関数型インターフェースの1つです。
java.util.functionパッケージの中に定義されていますので実際に見てみましょう。↓
1
2
3
4
|
@FunctionalInterface
public interface Predicate<T> {
boolean test(T arg0);
}
|
(実際はこんな短くないですが今回の話に関係しないので省略)
boolean型を返すメソッドが定義されています。
Tについて簡単に説明しますとPredicate<T>とtest(T arg0)は同じTを指しており、インスタンス化するときに<T>のところで型を定義します。
今回はIntegerを定義していますね。(型パラメータで調べるともっと分かるかも!)
1
|
if(pre.test(i))
|
Predicateインターフェースにあるtestメソッドが呼ばれています。
この時に呼ばれているtestメソッドはオーバーライドされた処理が実行されています。
1
|
outputNum(i -> i % 2 == 0);
|
関数型インターフェースの一番大きな利点です。
ラムダ式という記法で、この一行でPredicateインターフェースを継承した無名クラスのインスタンス化がされています。
これを従来のものに戻すと以下のようになります。↓
1
2
3
4
5
6
|
outputNum(new Predicate<Integer>() {
@Override
public boolean test(Integer i) {
return i % 2 == 1;
}
});
|
この文章をラムダ式にするとあんなに短くなるなんて驚きですね!
ラムダ式の簡単な説明をすると
1
|
引数 -> 処理;
|
引数は従来のtestメソッドの (Integer i) に当たり、処理が {return i % 2 == 1} に当たります。
- 引数が1つの時、型名(Integer)と()が省略可能
- 処理が1つの時、returnと{}が省略可能
こちらに良い記事があるので良ければ↓
【Java入門】Java8のラムダ式の使い方とメリットとは(Stream API)
解説終わり!
いかがだったでしょうか
関数型インターフェースが使えると今までのコードが目に見えてコンパクトに書けるようになると思います!また、今回は割愛しましたがStream apiが使えるとさらに劇的に!
今まで何行にも跨いで書いてた処理がたった1行にまとめられるかもしれませんよ!?
興味がある方はぜひ調べてみることをオススメします!
最後に
今回挙げたPredicateはJava8が事前に用意したbooleanを返す関数型インターフェースでした。
他にもjava.util.functionパッケージには同様に様々な型を返す関数型インターフェースが事前に用意されています。
返す型だけでなく、引数がないもの、複数あるものも用意されてますので
そのときどきによって用途にあった関数型インターフェースを使えるとベストかと思います。
また、Java8が事前に用意したものに限らず、以下の条件をクリアしていれば自身でも関数型インターフェースが定義できますので頭の片隅に置いとくと良いかもしれません(多分事前にあるもので足りてると思うけど…
- インターフェースである
- 抽象メソッドを1つだけ持っている(default、staticメソッドは対象外
- @FunctionalInterfaceがついている(コード上に記述されてなくても動きます