前書き
この記事では、例を使用して、Javaでのラムダ式、機能インターフェイス、パラメータ化された機能インターフェイス、およびStreamAPIでの使用について説明します。
ラムダ式はJava8で追加されました。それらの主な目的は、読みやすさを向上させ、コードの量を減らすことです。
しかし、ラムダに移る前に、機能インターフェイスを理解する必要があります。
機能的なインターフェイスとは何ですか?
Javaのインターフェースに1つだけの抽象メソッドが含まれている場合、それは機能的と呼ばれます。この単一のメソッドによって、インターフェイスの目的が決まります。
たとえば、java.langパッケージのRunnableインターフェイスは、run()メソッドが1つしかないため、機能します。
例1:Javaで機能インターフェイスを宣言する
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
//
double getValue();
}
上記の例では、MyInterfaceインターフェイスにはgetValue()という1つの抽象メソッドしかありません。したがって、このインターフェイスは機能します。
ここでは注釈を使用しましたFunctionalInterfaceこれは、コンパイラがインターフェイスが機能していることを理解するのに役立ちます。したがって、複数の抽象メソッドを持つことはできません。ただし、省略できます。
Java 7では、機能インターフェイスは単一抽象メソッド(SAM)として扱われていました。SAMは通常、匿名クラスを使用して実装されました。
例2:Javaで匿名クラスを使用してSAMを実装する
public class FunctionInterfaceTest {
public static void main(String[] args) {
//
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(" Runnable.")
}
}).start();
}
}
実行結果:
Runnable.
この例では、メソッドを呼び出すために匿名クラスを受け入れます。これは、Java 7でより少ないコード行でプログラムを作成するのに役立ちました。ただし、構文は非常に複雑で面倒なままでした。
Java 8は、SAMの機能を拡張し、さらに一歩進めました。ご存知のように、機能インターフェイスにはメソッドが1つしかないため、引数として渡すときにメソッドの名前を指定する必要はありません。これはまさにラムダ式が私たちにできることです。
ラムダ式の紹介
ラムダ式は本質的に匿名のクラスまたはメソッドです。ラムダ式はそれ自体では実行されません。代わりに、機能インターフェイスで定義されたメソッドを実装するために使用されます。
Javaでラムダ式を作成するにはどうすればよいですか?
Javaでは、ラムダ式の構文は次のとおりです。
(parameter list) -> lambda body
ここでは、新しい演算子(->)-ラムダ演算子を使用しました。おそらく、構文は少しトリッキーに思えます。いくつか例を見てみましょう。
次のような方法があるとしましょう。
double getPiValue() {
return 3.1415;
}
次のようなラムダを使用して記述できます。
() -> 3.1415
このメソッドにはパラメーターがありません。したがって、式の左側には空の括弧が含まれています。右側はラムダ式の本体であり、そのアクションを定義します。この場合、戻り値は3.1415です。
ラムダ式の種類
Javaでは、ラムダの本体は2つのタイプになります。
1.1行
() -> System.out.println("Lambdas are great");
2.ブロック(複数行)
() -> {
double pi = 3.1415;
return pi;
};
このタイプにより、ラムダ式は内部で複数の操作を持つことができます。これらの操作は、中括弧で囲み、その後にセミコロンを付ける必要があります。
注:複数行のラムダ式には、単一行の式とは異なり、常にreturnステートメントが必要です。
例3:ラムダ式ラムダ式
を使用してPiを返すJavaプログラムを作成してみましょう。
前述のように、ラムダ式は自動的に実行されません。むしろ、機能インターフェイスで宣言された抽象メソッドの実装を形成します。
したがって、最初に、機能インターフェイスについて説明する必要があります。
import java.lang.FunctionalInterface;
//
@FunctionalInterface
interface MyInterface{
//
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// MyInterface
MyInterface ref;
// -
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
実行結果:
Value of Pi = 3.1415
上記の例では:
- 1つの抽象メソッドgetPiValue()を含む機能インターフェイスMyInterfaceを作成しました。
- Mainクラス内で、MyInterfaceへの参照を宣言しました。インターフェイス参照を宣言することはできますが、そのオブジェクトを作成することはできないことに注意してください。
// MyInterface ref = new myInterface(); // MyInterface ref;
- 次に、ラムダ式をリンクに割り当てました
ref = () -> 3.1415; - 最後に、インターフェイス参照を使用してgetPiValue()メソッドを呼び出しました。
System.out.println("Value of Pi = " + ref.getPiValue());
パラメータ付きのラムダ式
これまで、パラメータなしでラムダ式を作成してきました。ただし、メソッドと同様に、ラムダはパラメーターを持つことができます。
(n) -> (n % 2) == 0
この例では、括弧内の変数nは、ラムダ式に渡されるパラメーターです。ラムダ本体はパラメータを受け取り、パリティをチェックします。
例4:パラメーターを使用してラムダ式を使用する
@FunctionalInterface
interface MyInterface {
//
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// MyInterface
// -
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
//
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
実行結果:
Lambda reversed = adbmaL
パラメータ化された機能インターフェイス
これまで、1つの値タイプのみを受け入れる機能インターフェイスを使用してきました。例えば:
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
上記の機能インターフェイスはStringのみを受け入れ、Stringを返します。ただし、インターフェイスを汎用にして、任意のデータタイプで使用できます。
例5:パラメータ化されたインターフェイスとラムダ式
//
@FunctionalInterface
interface GenericInterface<T> {
//
T func(T t);
}
public class Main {
public static void main( String[] args ) {
//
// String
//
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"));
//
// Integer
//
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
この例では、パラメーター化されたfunc()メソッドを含むパラメーター化された機能インターフェイスGenericInterfaceを作成しました。
次に、メインクラス内:
- GenericInterface <String> reverse-Stringで機能するインターフェースへのリンクを作成します。
- GenericInterface <Integer> factorial-Integerで機能するインターフェイスへのリンクを作成します。
ラムダ式とストリームAPI
JDK8は、新しいパッケージjava.util.streamを追加します。これにより、java開発者は、リストなどのコレクションの検索、フィルタリング、照合、連結、または操作などの操作を実行できます。
たとえば、データストリーム(この場合は文字列のリスト)があり、各文字列には国の名前とその都市が含まれています。これで、このデータストリームを処理して、ネパールの都市のみを選択できます。
これを行うには、StreamAPIとラムダ式を組み合わせて使用できます。
例6:StreamAPIでのラムダの使用
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
//
static List<String> places = new ArrayList<>();
//
public static List getPlaces(){
//
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:");
//
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));
ここでは、Stream APIのfilter()、map()、forEach()などのメソッドを使用します。これらのメソッドは、ラムダをパラメーターとして受け取ることができます。
また、上記の構文に基づいて独自の式を記述することもできます。これにより、コードの行数を減らすことができます。