Java Language
ラムダ式
サーチ…
前書き
ラムダ式は、式を使用して単一メソッドのインターフェイスを実装するための明確かつ簡潔な方法を提供します。それらを使用すると、作成および保守しなければならないコードの量を減らすことができます。匿名クラスと似ていますが、型情報はそれ自体ではありません。型推論が必要です。
メソッド参照は、式ではなく既存のメソッドを使用して関数インタフェースを実装します。彼らはラムダ家族にも属しています。
構文
- () - > {式を返す; } //値を返すための関数本体のゼロアリティ。
- () - >式//上記宣言の略語。式のセミコロンはありません。
- () - > {function-body} //操作を実行するラムダ式の副作用。
- parameterName - >式// 1つの引数のラムダ式。引数が1つしかないラムダ式では、かっこは削除できます。
- (type parameterName、Type secondParameterName、...) - >式//左側にリストされたパラメータを持つ式を評価するlambda
- (parameterName、secondParameterName、...) - >式//パラメータ名のパラメータ型を削除する省略表現。指定されたパラメータリストのサイズが期待される機能インタフェースのサイズのうちの1つ(そして1つだけ)と一致するコンパイラによって推論できるコンテキストでのみ使用できます。
ラムダ式を使ってコレクションを並べ替える
ソートリスト
Java 8以前では、リストをソートするときに、匿名(または名前付き)クラスでjava.util.Comparator
インタフェースを実装する必要がありました1 :
List<Person> people = ...
Collections.sort(
people,
new Comparator<Person>() {
public int compare(Person p1, Person p2){
return p1.getFirstName().compareTo(p2.getFirstName());
}
}
);
Java 8以降、匿名クラスはラムダ式に置き換えることができます。コンパイラが自動的に推論するので、パラメータp1
とp2
の型をp2
することができます。
Collections.sort(
people,
(p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName())
);
この例は、 Comparator.comparing
と::
ダブルコロン)記号を使用して表現されたメソッド参照を使用することで簡略化できます。
Collections.sort(
people,
Comparator.comparing(Person::getFirstName)
);
静的インポートを使用すると、これをより簡潔に表現することができますが、全体的な可読性が向上するかどうかは議論の余地があります。
import static java.util.Collections.sort;
import static java.util.Comparator.comparing;
//...
sort(people, comparing(Person::getFirstName));
このように構築されたコンパレータは、連鎖させることもできます。たとえば、名前を人で比較した後、同じ名前の人が存在する場合は、 thenComparing
メソッドも姓と比較します。
sort(people, comparing(Person::getFirstName).thenComparing(Person::getLastName));
1 - Collections.sort(...)は、 List
サブタイプであるコレクションに対してのみ機能することに注意してください。 Set
APIとCollection
APIは要素の順序付けを意味しません。
マップの並べ替え
同様の方法でHashMap
のエントリを値でソートすることができます。 ( LinkedHashMap
ターゲットとして使用する必要があることに注意してください。通常のHashMap
のキーは順序付けされていません)。
Map<String, Integer> map = new HashMap(); // ... or any other Map class
// populate the map
map = map.entrySet()
.stream()
.sorted(Map.Entry.<String, Integer>comparingByValue())
.collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue(),
(k, v) -> k, LinkedHashMap::new));
Javaラムダ入門
機能インタフェース
Lambdasは機能的なインタフェースでしか動作できません。これは1つの抽象メソッドを持つインタフェースです。機能インタフェースは、任意の数のdefault
またはstatic
メソッドを持つことができます。 (このため、Single Abstract Method InterfacesまたはSAM Interfacesと呼ばれることもあります)。
interface Foo1 {
void bar();
}
interface Foo2 {
int bar(boolean baz);
}
interface Foo3 {
String bar(Object baz, int mink);
}
interface Foo4 {
default String bar() { // default so not counted
return "baz";
}
void quux();
}
機能的なインタフェースを宣言するときに、 @FunctionalInterface
FunctionalInterfaceアノテーションを追加することができます。これには特別な効果はありませんが、このアノテーションが機能していないインタフェースに適用されている場合は、コンパイラエラーが生成され、インタフェースを変更しないことを通知する役割を果たします。
@FunctionalInterface
interface Foo5 {
void bar();
}
@FunctionalInterface
interface BlankFoo1 extends Foo3 { // inherits abstract method from Foo3
}
@FunctionalInterface
interface Foo6 {
void bar();
boolean equals(Object obj); // overrides one of Object's method so not counted
}
逆に、これは複数の抽象メソッドを持っているため、これは関数型インタフェースではありません 。
interface BadFoo {
void bar();
void quux(); // <-- Second method prevents lambda: which one should
// be considered as lambda?
}
これはメソッドを持たないため 、機能的なインターフェースでもありません。
interface BlankFoo2 { }
次のことに注意してください。あなたが持っていると仮定
interface Parent { public int parentMethod(); }
そして
interface Child extends Parent { public int ChildMethod(); }
それで、2つの指定されたメソッドがあるため、 Child
は機能的なインターフェースにはなりません 。
Java 8はまた、パッケージjava.util.function
内に多数の汎用テンプレート化機能インタフェースを提供しています。たとえば、組み込みのインタフェースPredicate<T>
は、型T
値を入力してboolean
を返す単一のメソッドをラップします。
ラムダ式
ラムダ式の基本構造は次のとおりです。
fi
はFunctionalInterface
を実装する匿名クラスに似たクラスのシングルトンインスタンスを保持し、そのメソッドの定義は{ System.out.println("Hello"); }
。言い換えれば、上記はほぼ次のものと同等です。
FunctionalInterface fi = new FunctionalInterface() {
@Override
public void theOneMethod() {
System.out.println("Hello");
}
};
ラムダでは、 this
、 super
またはtoString()
ような式の意味は、新しく作成されたオブジェクトではなく、割り当てが行われるクラスを参照するため、ラムダは匿名クラスと "ほとんど同等"です。
関数のインタフェースには抽象メソッドが1つしかなくてはならないため、ラムダを使用するときにメソッドの名前を指定することはできませんが、そうする必要はありません。
ラムダの型が確実でない場合(例えば、オーバーロードされたメソッド)、ラムダにキャストを追加して、コンパイラにその型がどうあるべきかを伝えることができます:
Object fooHolder = (Foo1) () -> System.out.println("Hello");
System.out.println(fooHolder instanceof Foo1); // returns true
機能インタフェースの単一のメソッドがパラメータをとる場合、これらのローカル正式名はラムダの角括弧の間に現れます。パラメータの型を宣言する必要はなく、インタフェースから取ってくるので返す必要もあります(ただし、必要に応じてパラメータの型を宣言するのはエラーではありません)。したがって、これらの2つの例は同等です。
Foo2 longFoo = new Foo2() {
@Override
public int bar(boolean baz) {
return baz ? 1 : 0;
}
};
Foo2 shortFoo = (x) -> { return x ? 1 : 0; };
関数の引数が1つしかない場合は、引数の前後のかっこは省略できます。
Foo2 np = x -> { return x ? 1 : 0; }; // okay
Foo3 np2 = x, y -> x.toString() + y // not okay
暗黙の返品
ラムダの内部に配置されたコードがステートメントではなくJava 式である場合、それは式の値を返すメソッドとして扱われます。したがって、次の2つは同等です。
IntUnaryOperator addOneShort = (x) -> (x + 1);
IntUnaryOperator addOneLong = (x) -> { return (x + 1); };
ローカル変数へのアクセス(バリュークローズ)
ラムダは匿名クラスの構文上の省略形なので、囲みスコープ内のローカル変数にアクセスするのと同じ規則に従います。変数はラムダ内でfinal
ものとして扱われ、変更されてはならない。
IntUnaryOperator makeAdder(int amount) {
return (x) -> (x + amount); // Legal even though amount will go out of scope
// because amount is not modified
}
IntUnaryOperator makeAccumulator(int value) {
return (x) -> { value += x; return value; }; // Will not compile
}
このように変更する変数をラップする必要がある場合は、変数のコピーを保持する通常のオブジェクトを使用する必要があります。 ラムダ式を使用したJava Closureで詳しく説明しています。
ラムダ受け入れ
ラムダはインターフェイスの実装なので、メソッドがラムダを受け入れるために特別な処理をする必要はありません。関数インターフェイスをとる関数はラムダを受け入れることもできます。
public void passMeALambda(Foo1 f) {
f.bar();
}
passMeALambda(() -> System.out.println("Lambda called"));
ラムダ式の型
ラムダ式はそれ自身では特定の型を持たない。戻り値の型とともにパラメータの型と数がいくつかの型情報を伝えることは事実ですが、そのような情報はどの型に割り当てることができるかを制約します。ラムダは、次のいずれかの方法で、機能的なインターフェイスタイプに割り当てられたときにタイプを受け取ります。
- 例えば
myPredicate = s -> s.isEmpty()
ように、関数型への直接代入 - 機能型を持つパラメータとして渡します。たとえば、
stream.filter(s -> s.isEmpty())
- 関数型を返す関数から返す。例えば、
return s -> s.isEmpty()
- それを機能型にキャストする、例えば
(Predicate<String>) s -> s.isEmpty()
機能タイプへのそのような割り当てがなされるまで、ラムダは明確なタイプを有さない。例として、ラムダ式o -> o.isEmpty()
考えてみo -> o.isEmpty()
。同じラムダ式を多くの異なる関数型に割り当てることができます。
Predicate<String> javaStringPred = o -> o.isEmpty();
Function<String, Boolean> javaFunc = o -> o.isEmpty();
Predicate<List> javaListPred = o -> o.isEmpty();
Consumer<String> javaStringConsumer = o -> o.isEmpty(); // return value is ignored!
com.google.common.base.Predicate<String> guavaPredicate = o -> o.isEmpty();
ラムダ式は同じように見えますが、互いに割り当てることはできませんが、ここで示した例は完全に異なる型のものです。
メソッド参照
メソッド参照では、匿名ラムダ式の代わりに引数として渡すことができる、互換性のある関数型インターフェイスに準拠する、あらかじめ定義された静的メソッドまたはインスタンスメソッドを使用できます。
モデルがあるとします。
class Person {
private final String name;
private final String surname;
public Person(String name, String surname){
this.name = name;
this.surname = surname;
}
public String getName(){ return name; }
public String getSurname(){ return surname; }
}
List<Person> people = getSomePeople();
インスタンスメソッド参照(任意のインスタンスへ)
people.stream().map(Person::getName)
等価ラムダ:
people.stream().map(person -> person.getName())
この例では、タイプPerson
インスタンス・メソッドgetName()
へのメソッド参照が渡されています。コレクション型であることがわかっているので、インスタンスのメソッド(後で分かる)が呼び出されます。
インスタンスメソッド参照(特定のインスタンスへの参照)
people.forEach(System.out::println);
System.out
はPrintStream
インスタンスなので、この特定のインスタンスへのメソッド参照が引数として渡されます。
等価ラムダ:
people.forEach(person -> System.out.println(person));
静的メソッド参照
ストリームを変換する場合は、静的メソッドへの参照を適用することもできます。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.stream().map(String::valueOf)
この例では、 String
型のstatic valueOf()
メソッドへの参照を渡します。したがって、コレクション内のインスタンスオブジェクトは、 valueOf()
引数として渡されます。
等価ラムダ:
numbers.stream().map(num -> String.valueOf(num))
コンストラクタへの参照
List<String> strings = Arrays.asList("1", "2", "3");
strings.stream().map(Integer::new)
コレクションの要素を収集する方法については、「ストリームの要素をコレクションに読み込む」を参照してください。
Integer
型の単一のString引数コンストラクタがここで使用され、引数として指定された文字列を渡して整数を構築します。この場合、文字列が数値を表す限り、ストリームは整数にマップされます。等価ラムダ:
strings.stream().map(s -> new Integer(s));
カンニングペーパー
メソッド参照形式 | コード | 等価ラムダ |
---|---|---|
静的メソッド | TypeName::method | (args) -> TypeName.method(args) |
非静的メソッド(インスタンス*の場合) | instance::method | (args) -> instance.method(args) |
非静的メソッド(インスタンスなし) | TypeName::method | (instance, args) -> instance.method(args) |
コンストラクタ** | TypeName::new | (args) -> new TypeName(args) |
配列コンストラクタ | TypeName[]::new | (int size) -> new TypeName[size] |
* instance
は、インスタンスへの参照を評価する任意の式です。例えば、 getInstance()::method
、 this::method
** TypeName
が非静的な内部クラスである場合、コンストラクタ参照は外部クラスインスタンスのスコープ内でのみ有効です
複数のインタフェースの実装
ラムダ式を複数のインターフェイスで実装したい場合があります。これは、マッパーインタフェース( java.io.Serializableなど)では、抽象メソッドを追加しないため、ほとんどの場合便利です。
たとえば、カスタムComparator
TreeSet
を作成し、シリアル化してネットワーク経由で送信したいとします。簡単なアプローチ:
TreeSet<Long> ts = new TreeSet<>((x, y) -> Long.compare(y, x));
コンパレータのラムダはSerializable
実装していないので動作しません。交差タイプを使用し、このラムダをシリアライズ可能にする必要があることを明示的に指定することで、これを修正できます。
TreeSet<Long> ts = new TreeSet<>(
(Comparator<Long> & Serializable) (x, y) -> Long.compare(y, x));
ほとんどの場合、シリアル化が可能なApache Sparkなどのフレームワークを使用している場合など、頻繁に交差タイプを使用している場合は、空のインターフェースを作成して、代わりにコードで使用できます。
public interface SerializableComparator extends Comparator<Long>, Serializable {}
public class CustomTreeSet {
public CustomTreeSet(SerializableComparator comparator) {}
}
これにより、渡されたコンパレータが直列化可能であることが保証されます。
ラムダと実行パターン
簡単なシナリオでは、lambdaをFunctionalInterfaceとして使用する良い例がいくつかあります。ラムダによって改善できるかなり一般的なユースケースは、いわゆるExecute-Aroundパターンです。このパターンでは、ユースケース固有のコードを取り巻く複数のシナリオで必要となる一連の標準セットアップ/ティアダウンコードが用意されています。これの一般的な例は、ファイルio、データベースio、try / catchブロックです。
interface DataProcessor {
void process( Connection connection ) throws SQLException;;
}
public void doProcessing( DataProcessor processor ) throws SQLException{
try (Connection connection = DBUtil.getDatabaseConnection();) {
processor.process(connection);
connection.commit();
}
}
ラムダでこのメソッドを呼び出すと、次のようになります。
public static void updateMyDAO(MyVO vo) throws DatabaseException {
doProcessing((Connection conn) -> MyDAO.update(conn, ObjectMapper.map(vo)));
}
これはI / O操作に限定されません。これは、同様の設定/解除タスクが小さな変更で適用可能なあらゆるシナリオに適用できます。このパターンの主な利点は、コードの再利用とDRY(繰り返しはしないでください)を強制することです。
ラムダ式を独自の関数インターフェイスで使用する
Lambdaは、単一メソッドインタフェースのインラインインプリメンテーションコードと、通常の変数で行ってきたようにそれらを渡す機能を提供することを意図しています。我々はそれらを機能的インタフェースと呼ぶ。
たとえば、匿名クラスにRunnableを記述し、スレッドを開始すると、次のようになります。
//Old way
new Thread(
new Runnable(){
public void run(){
System.out.println("run logic...");
}
}
).start();
//lambdas, from Java 8
new Thread(
()-> System.out.println("run logic...")
).start();
上記のように、カスタムインターフェースがあるとしましょう:
interface TwoArgInterface {
int operate(int a, int b);
}
どのようにラムダを使用してコード内にこのインターフェイスの実装を提供しますか?上記のRunnableの例と同じです。以下のドライバプログラムを参照してください:
public class CustomLambda {
public static void main(String[] args) {
TwoArgInterface plusOperation = (a, b) -> a + b;
TwoArgInterface divideOperation = (a,b)->{
if (b==0) throw new IllegalArgumentException("Divisor can not be 0");
return a/b;
};
System.out.println("Plus operation of 3 and 5 is: " + plusOperation.operate(3, 5));
System.out.println("Divide operation 50 by 25 is: " + divideOperation.operate(50, 25));
}
}
`return`は外側のメソッドではなく、ラムダからのみ返ります。
return
メソッドは、外側のメソッドではなくラムダからのみ返されます。
これはScalaとKotlinとは異なることに注意してください!
void threeTimes(IntConsumer r) {
for (int i = 0; i < 3; i++) {
r.accept(i);
}
}
void demo() {
threeTimes(i -> {
System.out.println(i);
return; // Return from lambda to threeTimes only!
});
}
などの組み込み構造のように、独自の言語構造を記述しようとするとき、これは予期しない動作につながる可能性for
のループがreturn
動作が異なります:
void demo2() {
for (int i = 0; i < 3; i++) {
System.out.println(i);
return; // Return from 'demo2' entirely
}
}
ScalaとKotlinでは、 demo
とdemo
demo2
は両方とも0
ます。しかしこれは一貫していません 。 Javaのアプローチは、リファクタリングとクラスの使用と一貫しています。つまり、コードの一番上のreturn
であり、以下のコードは同じように動作します。
void demo3() {
threeTimes(new MyIntConsumer());
}
class MyIntConsumer implements IntConsumer {
public void accept(int i) {
System.out.println(i);
return;
}
}
したがって、Javaのreturn
はクラスメソッドとリファクタリングとより一貫しますが、組み込み関数のfor
やwhile
では、これらは特別なままです。
このため、次の2つはJavaで同等です。
IntStream.range(1, 4)
.map(x -> x * x)
.forEach(System.out::println);
IntStream.range(1, 4)
.map(x -> { return x * x; })
.forEach(System.out::println);
さらに、Javaではtry-with-resourceの使用が安全です。
class Resource implements AutoCloseable {
public void close() { System.out.println("close()"); }
}
void executeAround(Consumer<Resource> f) {
try (Resource r = new Resource()) {
System.out.print("before ");
f.accept(r);
System.out.print("after ");
}
}
void demo4() {
executeAround(r -> {
System.out.print("accept() ");
return; // Does not return from demo4, but frees the resource.
});
}
before accept() after close()
に印刷されます。 ScalaとKotlinのセマンティクスでは、try-with-resourcesは閉じられませんが、 before accept()
のみ印刷されます。
ラムダ式によるJavaクローズ
ラムダ式が囲むスコープ(グローバルまたはローカル)の変数を参照すると、ラムダクロージャが作成されます。これを行うためのルールは、インラインメソッドと匿名クラスのルールと同じです。
ラムダ内で使用される囲みスコープのローカル変数は、 final
なければなりません。 Java 8(ラムダをサポートする最も初期のバージョン)では、外部コンテキストでfinal
宣言する必要はありませんが、そのように扱わなければなりません。例えば:
int n = 0; // With Java 8 there is no need to explicit final
Runnable r = () -> { // Using lambda
int i = n;
// do something
};
これは、 n
変数の値が変更されない限り有効です。ラムダの内部または外部の変数を変更しようとすると、次のコンパイルエラーが発生します。
"ラムダ式から参照されるローカル変数は、 最終的または効果的に最終的でなければならない"。
例えば:
int n = 0;
Runnable r = () -> { // Using lambda
int i = n;
// do something
};
n++; // Will generate an error.
ラムダ内で変化する変数を使用する必要がある場合、通常の方法は変数のfinal
コピーを宣言してそのコピーを使用することです。例えば
int n = 0;
final int k = n; // With Java 8 there is no need to explicit final
Runnable r = () -> { // Using lambda
int i = k;
// do something
};
n++; // Now will not generate an error
r.run(); // Will run with i = 0 because k was 0 when the lambda was created
もちろん、ラムダ本体には元の変数への変更が表示されません。
Javaは真のクロージャをサポートしていないことに注意してください。 Javaラムダは、インスタンス化された環境の変化を見ることができる方法で作成することはできません。環境を監視したり変更したりするクロージャを実装する場合は、通常のクラスを使用してシミュレートする必要があります。例えば:
// Does not compile ...
public IntUnaryOperator createAccumulator() {
int value = 0;
IntUnaryOperator accumulate = (x) -> { value += x; return value; };
return accumulate;
}
上記の例は、前述の理由でコンパイルされません。次のようにコンパイルエラーを回避することができます:
// Compiles, but is incorrect ...
public class AccumulatorGenerator {
private int value = 0;
public IntUnaryOperator createAccumulator() {
IntUnaryOperator accumulate = (x) -> { value += x; return value; };
return accumulate;
}
}
問題は、インスタンスが機能し、ステートレスでなければならないことを示すIntUnaryOperator
インターフェイスの設計規約がIntUnaryOperator
れてIntUnaryOperator
ことです。そのようなクロージャーが機能オブジェクトを受け入れる組み込み関数に渡された場合、クラッシュや誤動作の原因となりがちです。変更可能な状態をカプセル化するクロージャは、通常のクラスとして実装する必要があります。例えば。
// Correct ...
public class Accumulator {
private int value = 0;
public int accumulate(int x) {
value += x;
return value;
}
}
ラムダ - リスナーの例
匿名クラスのリスナー
Java 8より前では、次のコードに示すように、匿名クラスを使用してJButtonのクリックイベントを処理することは非常に一般的です。この例では、 btn.addActionListener
のスコープ内に匿名リスナーを実装する方法を示します。
JButton btn = new JButton("My Button");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button was pressed");
}
});
ラムダリスナー
ActionListener
インタフェースはactionPerformed()
メソッドを1つしか定義していないため、定型表現のコードを置き換えるためにラムダ式を使用する場所があることを意味する機能的なインタフェースです。上記の例は、次のようにラムダ式を使って書き直すことができます:
JButton btn = new JButton("My Button");
btn.addActionListener(e -> {
System.out.println("Button was pressed");
});
伝統的なスタイルからラムダスタイルまで
伝統的な方法
interface MathOperation{
boolean unaryOperation(int num);
}
public class LambdaTry {
public static void main(String[] args) {
MathOperation isEven = new MathOperation() {
@Override
public boolean unaryOperation(int num) {
return num%2 == 0;
}
};
System.out.println(isEven.unaryOperation(25));
System.out.println(isEven.unaryOperation(20));
}
}
ラムダスタイル
- クラス名と機能インタフェース本体を削除します。
public class LambdaTry {
public static void main(String[] args) {
MathOperation isEven = (int num) -> {
return num%2 == 0;
};
System.out.println(isEven.unaryOperation(25));
System.out.println(isEven.unaryOperation(20));
}
}
- オプションの型宣言
MathOperation isEven = (num) -> {
return num%2 == 0;
};
- 単一パラメータの場合は、パラメータの周りの括弧(オプション)
MathOperation isEven = num -> {
return num%2 == 0;
};
- オプションの中カッコ(関数本体に1行しかない場合)
- オプションのreturnキーワード。関数本体に1行しかない場合
MathOperation isEven = num -> num%2 == 0;
ラムダとメモリ使用率
Javaラムダはクロージャであるため、囲むレキシカルスコープ内の変数の値を「取得」できます。すべてのラムダが何かをキャプチャしているわけではありませんが、 s -> s.length()
ような単純なラムダは何も取得せず、 ステートレスキャプチャラムダと呼ばれ、キャプチャされた変数を保持するために一時オブジェクトが必要です。このコードスニペットでは、lambda () -> j
はキャプチャラムダであり、オブジェクトが評価されるときにオブジェクトが割り当てられる可能性があります。
public static void main(String[] args) throws Exception {
for (int i = 0; i < 1000000000; i++) {
int j = i;
doSomethingWithLambda(() -> j);
}
}
new
キーワードがスニペットのどこにも現れないのですぐにはわからないかもしれませんが、このコードでは、 () -> j
ラムダ式のインスタンスを表すために1,000,000,000の別々のオブジェクトを作成しなければなりません。ただし、Java 1の将来のバージョンでは、実行時にラムダインスタンスが再利用されたり、他の方法で表現されたりするように、これを最適化できる可能性があります。
たとえば、Java 9では、Javaビルドシーケンスにオプションの「リンク」フェーズが導入されており、このようなグローバルな最適化を行う機会が提供されます。
ラムダ式と述語を使ってリストから特定の値を取得する
Java 8からは、ラムダ式と述語を使用できます。
例:ラムダ式とa述語を使用して、リストから特定の値を取得します。この例では、18歳以上であればすべての人物がその事実をプリントアウトされます。
個人クラス:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() { return age; }
public String getName() { return name; }
}
java.util.function.Predicateパッケージからの組み込みインターフェースPredicateは、 boolean test(T t)
メソッドを持つ機能インターフェースです。
使用例:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class LambdaExample {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Jeroen", 20));
personList.add(new Person("Jack", 5));
personList.add(new Person("Lisa", 19));
print(personList, p -> p.getAge() >= 18);
}
private static void print(List<Person> personList, Predicate<Person> checker) {
for (Person person : personList) {
if (checker.test(person)) {
System.out.print(person + " matches your expression.");
} else {
System.out.println(person + " doesn't match your expression.");
}
}
}
}
print(personList, p -> p.getAge() >= 18);
メソッドはラムダ式をとります(Predicateはパラメータとして使用されるため)。必要な式を定義できます。チェッカーのテストメソッドは、この式が正しいかどうかをチェックします: checker.test(person)
。
例えば、 print(personList, p -> p.getName().startsWith("J"));
ように、これを他のものに簡単に変更することができますprint(personList, p -> p.getName().startsWith("J"));
。これは、人の名前が「J」で始まるかどうかをチェックします。