Java ラムダ式
Java ラムダ式
この記事では、Java ラムダ式と、関数型インターフェース、汎用関数型インターフェース、およびストリーム API でのラムダ式の使用について、例を使用して学習します。
ラムダ式は、Java 8 で初めて導入されました。その主な目的は、言語の表現力を高めることです。
しかし、ラムダに入る前に、まず関数型インターフェースを理解する必要があります.
機能インターフェースとは?
Java インターフェースに抽象メソッドが 1 つだけ含まれている場合、それは機能インターフェースと呼ばれます。この 1 つのメソッドだけが、インターフェースの意図された目的を指定します。
たとえば、Runnable
パッケージ java.lang
のインターフェース; 1 つのメソッド、つまり run()
のみを構成するため、関数型インターフェイスです。 .
例 1:Java で機能インターフェースを定義する
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
// the single abstract method
double getValue();
}
上記の例では、インターフェイス MyInterface には抽象メソッド getValue() が 1 つだけあります。したがって、これは機能的なインターフェースです。
ここでは、注釈 @FunctionalInterface
を使用しています。 .アノテーションは、インターフェースが機能インターフェースであることを Java コンパイラーに強制的に示します。したがって、複数の抽象メソッドを持つことはできません。ただし、必須ではありません。
Java 7 では、機能インターフェースは単一抽象メソッドまたは SAM と見なされていました。 タイプ。 SAM は、一般的に Java 7 の匿名クラスで実装されました。
例 2:Java で匿名クラスを使用して SAM を実装する
public class FunctionInterfaceTest {
public static void main(String[] args) {
// anonymous class
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I just implemented the Runnable Functional Interface.");
}
}).start();
}
}
出力 :
I just implemented the Runnable Functional Interface.
ここで、無名クラスをメソッドに渡すことができます。これは、Java 7 でより少ないコードでプログラムを作成するのに役立ちます。ただし、構文は依然として難しく、多くの余分なコード行が必要でした。
Java 8 は、さらに一歩進んで SAM の能力を拡張しました。関数型インターフェースにはメソッドが 1 つしかないことがわかっているので、引数として渡すときにそのメソッドの名前を定義する必要はありません。ラムダ式はまさにそれを可能にします。
ラムダ式の概要
ラムダ式は、本質的に、匿名または名前のないメソッドです。ラムダ式は単独では実行されません。代わりに、機能インターフェースによって定義されたメソッドを実装するために使用されます。
Java でラムダ式を定義する方法
Java でラムダ式を定義する方法は次のとおりです。
(parameter list) -> lambda body
new 演算子 (->
) は、アロー演算子またはラムダ演算子として知られています。現時点では、構文が明確でない可能性があります。いくつかの例を見てみましょう。
次のようなメソッドがあるとします:
double getPiValue() {
return 3.1415;
}
ラムダ式を使用してこのメソッドを次のように記述できます。
() -> 3.1415
ここで、メソッドにはパラメーターがありません。したがって、演算子の左側には空のパラメーターが含まれます。右側は、ラムダ式のアクションを指定するラムダ本体です。この場合、値 3.1415 を返します。
ラムダ ボディのタイプ
Java では、ラムダ本体には 2 つのタイプがあります。
<強い>1.表情がひとつの身体
() -> System.out.println("Lambdas are great");
このタイプのラムダ本体は式本体として知られています。
<強い>2.コードのブロックで構成される本文。
() -> {
double pi = 3.1415;
return pi;
};
このタイプのラムダ本体は、ブロック本体として知られています。ブロック本体により、ラムダ本体に複数のステートメントを含めることができます。これらのステートメントは中括弧で囲まれており、中括弧の後にセミコロンを追加する必要があります.
注意 :ブロック本体の場合、本体が値を返す場合は return ステートメントを使用できます。ただし、式本体には return ステートメントは必要ありません。
例 3:ラムダ式
ラムダ式を使用して Pi の値を返す Java プログラムを書きましょう。
前述のとおり、ラムダ式は単独では実行されません。むしろ、機能インターフェースによって定義された抽象メソッドの実装を形成します。
したがって、最初に機能インターフェースを定義する必要があります。
import java.lang.FunctionalInterface;
// this is functional interface
@FunctionalInterface
interface MyInterface{
// abstract method
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
MyInterface ref;
// lambda expression
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
出力 :
Value of Pi = 3.1415
上記の例では、
- MyInterface という名前の機能インターフェースを作成しました .
getPiValue()
という名前の単一の抽象メソッドが含まれています - メインの内部 クラスで、MyInterface への参照を宣言しました .インターフェイスの参照を宣言することはできますが、インターフェイスをインスタンス化することはできないことに注意してください。あれは、
// it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
- 次に、参照にラムダ式を割り当てました。
ref = () -> 3.1415;
- 最後に、メソッド
getPiValue()
を呼び出します リファレンス インターフェイスを使用します。いつ
System.out.println("Value of Pi = " + ref.getPiValue());
パラメータ付きラムダ式
これまで、パラメーターなしでラムダ式を作成してきました。ただし、メソッドと同様に、ラムダ式にもパラメーターを含めることができます。たとえば、
(n) -> (n%2)==0
ここで、括弧内の変数 n は、ラムダ式に渡されるパラメーターです。ラムダ本体はパラメーターを受け取り、それが偶数か奇数かをチェックします。
例 4:パラメータでラムダ式を使用する
@FunctionalInterface
interface MyInterface {
// abstract method
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
// assign a lambda expression to the reference
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
// call the method of the interface
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
出力 :
Lambda reversed = adbmaL
汎用機能インターフェース
これまで、1 つのタイプの値のみを受け入れる関数型インターフェースを使用してきました。たとえば、
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
上記の機能インターフェースは String
のみを受け入れます String
を返します .ただし、関数インターフェイスを汎用にすることで、あらゆるデータ型を受け入れることができます。ジェネリックについてよくわからない場合は、Java ジェネリックにアクセスしてください。
例 5:ジェネリック関数型インターフェイスとラムダ式
// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {
// generic method
T func(T t);
}
// GenericLambda.java
public class Main {
public static void main( String[] args ) {
// declare a reference to GenericInterface
// the GenericInterface operates on String data
// assign a lambda expression to it
GenericInterface<String> reverse = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
System.out.println("Lambda reversed = " + reverse.func("Lambda"));
// declare another reference to GenericInterface
// the GenericInterface operates on Integer data
// assign a lambda expression to it
GenericInterface<Integer> factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++)
result = i * result;
return result;
};
System.out.println("factorial of 5 = " + factorial.func(5));
}
}
出力 :
Lambda reversed = adbmaL factorial of 5 = 120
上記の例では、GenericInterface という名前の汎用機能インターフェースを作成しました。 . func()
という名前の汎用メソッドが含まれています .
ここで、Main クラス内で、
GenericInterface<String> reverse
- インターフェイスへの参照を作成します。インターフェイスはString
で動作するようになりました データの種類GenericInterface<Integer> factorial
- インターフェイスへの参照を作成します。この場合、インターフェースはInteger
で動作します データの種類
ラムダ式とストリーム API
新しい java.util.stream パッケージが JDK8 に追加され、Java 開発者は Lists
のようなコレクションの検索、フィルター、マップ、縮小、または操作などの操作を実行できます。 .
たとえば、データのストリームがあります (この場合は List
String
の ) ここで、各文字列は国名とその国の場所の組み合わせです。これで、このデータ ストリームを処理して、ネパールから場所のみを取得できます。
このため、ストリーム API とラムダ式を組み合わせて、ストリーム内で一括操作を実行できます。
例 6:Stream API でラムダを使用するデモ
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
// create an object of list using ArrayList
static List<String> places = new ArrayList<>();
// preparing our data
public static List getPlaces(){
// add places and country to the list
places.add("Nepal, Kathmandu");
places.add("Nepal, Pokhara");
places.add("India, Delhi");
places.add("USA, New York");
places.add("Africa, Nigeria");
return places;
}
public static void main( String[] args ) {
List<String> myPlaces = getPlaces();
System.out.println("Places from Nepal:");
// Filter places from Nepal
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
}
}
出力 :
Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA
上記の例では、次のステートメントに注目してください。
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
ここでは、filter()
のようなメソッドを使用しています。 、 map()
と forEach()
ストリーム API の。これらのメソッドはラムダ式を入力として受け取ることができます。
上記で学んだ構文に基づいて、独自の式を定義することもできます。これにより、上記の例で見たようにコード行を大幅に減らすことができます。
Java