Javaでメソッド参照を開始する

ラムダとともに、Java SE8はメソッド参照をJava言語にもたらしました。このチュートリアルでは、Javaでのメソッド参照の概要を説明し、Javaコード例でそれらの使用を開始します。チュートリアルの終わりまでに、メソッド参照を使用してクラスの静的メソッド、バインドおよび非バインドの非静的メソッド、コンストラクターを参照する方法、およびそれらを使用してスーパークラスと現在のクラスのインスタンスメソッドを参照する方法を理解します。タイプ。また、多くのJava開発者が、匿名クラスのよりクリーンでシンプルな代替手段としてラムダ式とメソッド参照を採用している理由も理解できます。

このチュートリアルのコード例はJDK12と互換性があることに注意してください。

ダウンロードコードを取得するこのチュートリアルのサンプルアプリケーションのソースコードをダウンロードします。JavaWorld用にJeffFriesenによって作成されました。

メソッドリファレンス:入門書

以前のJava101チュートリアルでは、ラムダ式を紹介しました。ラムダ式は、関数型インターフェイスのインスタンスとして扱うことができる匿名メソッドを定義するために使用されます。ラムダ式は、既存のメソッドを呼び出すだけの場合があります。たとえば、次のコードフラグメントは、ラムダを使用して、ラムダの単一引数でSystem.outvoid println(s)メソッドを呼び出します-sのタイプはまだ不明です。

(s) -> System.out.println(s)

ラムダは(s)、正式なパラメーターリストと、そのSystem.out.println(s)式がsの値を標準出力ストリームに出力するコード本体として表示されます。明示的なインターフェイスタイプはありません。代わりに、コンパイラは、インスタンス化する機能インターフェイスを周囲のコンテキストから推測します。たとえば、次のコードフラグメントについて考えてみます。

Consumer consumer = (s) -> System.out.println(s);

コンパイラーは前の宣言を分析し、java.util.function.Consumer事前定義された関数型インターフェースのvoid accept(T t)メソッドがラムダの仮パラメーター・リスト((s))と一致することを判別します。また、と判断したaccept()void戻り値の型が一致println()void戻り値の型。したがって、ラムダはにバインドさConsumerます。

より具体的には、ラムダはにバインドされていConsumerます。コンパイラはコードを生成するため、Consumer'svoid accept(String s)メソッドを呼び出すと、文字列引数が' sメソッドsに渡されます。この呼び出しを以下に示します。System.outvoid println(String s)

consumer.accept("Hello"); // Pass "Hello" to lambda body. Print Hello to standard output.

キーストロークを節約するために、ラムダを既存のメソッドへのコンパクトな参照であるメソッド参照に置き換えることができます。例えば、以下のコードフラグメント置き換え(String s) -> System.out.println(s)System.out::println::が意味がことSystem.outvoid println(String s)方法が参照されています。

Consumer consumer2 = System.out::println; // The method reference is shorter. consumer2.accept("Hello"); // Pass "Hello" to lambda body. Print Hello to standard output.

コンパイラーは、Consumerこのパラメーター化された型のjava.lang.String実際の型引数がTで置き換えられvoid accept(T t)、ラムダ本体のSystem.out.println()メソッド呼び出しの単一パラメーターの型でもあることに基づいてこのリストを推測できるため、前のメソッド参照の正式なパラメーターリストを指定する必要はありません。

メソッドリファレンスの詳細

メソッド参照が既存の方法からのラムダを作成するための構文的ショートカットです。実装本体を提供する代わりに、メソッド参照は既存のクラスまたはオブジェクトのメソッドを参照します。ラムダと同様に、メソッド参照にはターゲット型が必要です。

メソッド参照を使用して、クラスの静的メソッド、バインドおよび非バインドの非静的メソッド、およびコンストラクターを参照できます。メソッド参照を使用して、スーパークラスおよび現在のクラスタイプのインスタンスメソッドを参照することもできます。これらのメソッド参照カテゴリのそれぞれを紹介し、それらが小さなデモでどのように使用されるかを示します。

メソッドリファレンスの詳細

このセクションを読んだ後、Java 8のメソッド参照(Toby Weston、2014年2月)をチェックして、バインドおよび非バインドの非静的メソッドコンテキストでのメソッド参照の詳細を確認してください。

静的メソッドへの参照

静的メソッド参照は、特定のクラスの静的メソッドを参照します。その構文はです。ここで、クラスを識別し、静的メソッドを識別します。例はです。リスト1は、静的メソッド参照を示しています。className::staticMethodNameclassNamestaticMethodNameInteger::bitCount

リスト1.MRDemo.java(バージョン1)

import java.util.Arrays; import java.util.function.Consumer; public class MRDemo { public static void main(String[] args) { int[] array = { 10, 2, 19, 5, 17 }; Consumer consumer = Arrays::sort; consumer.accept(array); for (int i = 0; i < array.length; i++) System.out.println(array[i]); System.out.println(); int[] array2 = { 19, 5, 14, 3, 21, 4 }; Consumer consumer2 = (a) -> Arrays.sort(a); consumer2.accept(array2); for (int i = 0; i < array2.length; i++) System.out.println(array2[i]); } }

リスト1のmain()メソッドは、java.util.Arraysクラスのstatic void sort(int[] a)メソッドを介して整数配列のペアをソートします。これは、静的メソッド参照および同等のラムダ式コンテキストに表示されます。配列をソートした後、forループはソートされた配列の内容を標準出力ストリームに出力します。

メソッド参照またはラムダを使用する前に、関数型インターフェースにバインドする必要があります。Consumerメソッド参照/ラムダ要件を満たす、事前定義された関数型インターフェースを使用しています。並べ替え操作は、並べ替える配列をConsumeraccept()メソッドに渡すことから始まります。

リスト1(javac MRDemo.java)をコンパイルし、アプリケーション(java MRDemo)を実行します。次の出力が表示されます。

2 5 10 17 19 3 4 5 14 19 21

バインドされた非静的メソッドへの参照

結合した非静的メソッド参照はに結合しています非静的メソッドを指すレシーバオブジェクト。その構文はです。ここで、レシーバーを識別し、インスタンスメソッドを識別します。例はです。リスト2は、バインドされた非静的メソッド参照を示しています。objectName::instanceMethodNameobjectNameinstanceMethodNames::trim

リスト2.MRDemo.java(バージョン2)

import java.util.function.Supplier; public class MRDemo { public static void main(String[] args) { String s = "The quick brown fox jumped over the lazy dog"; print(s::length); print(() -> s.length()); print(new Supplier() { @Override public Integer get() { return s.length(); // closes over s } }); } public static void print(Supplier supplier) { System.out.println(supplier.get()); } }

リスト2のmain()メソッドは、String変数に文字列を割り当ててから、このメソッドの引数としてこの文字列の長さを取得する機能を備えsprint()クラスメソッドを呼び出します。print()メソッド参照(s::length-length()はにバインドされていますs)、同等のラムダ、および同等の匿名クラスコンテキストで呼び出されます。

事前定義された関数型インターフェースprint()を使用するように定義しました。java.util.function.Supplierそのget()メソッドは結果のサプライヤーを返します。この場合、にSupplier渡されたインスタンスは、;を返すメソッドをprint()実装します。この長さを出力します。get()s.length()print()

s::lengthを閉じるクロージャを導入しますs。これは、ラムダの例でより明確に確認できます。ラムダには引数がないため、の値はs囲んでいるスコープからのみ使用できます。したがって、ラムダ本体はを閉じるクロージャですs。匿名クラスの例は、これをさらに明確にします。

リスト2をコンパイルして、アプリケーションを実行します。次の出力が表示されます。

44 44 44

バインドされていない非静的メソッドへの参照

結合していない非静的メソッド参照は、レシーバ・オブジェクトにバインドされていない非静的メソッドを指します。その構文はです。ここで、インスタンスメソッドを宣言するクラスを識別し、インスタンスメソッドを識別します。例はです。className::instanceMethodNameclassNameinstanceMethodNameString::toLowerCase

String::toLowerCaseクラスの非静的String toLowerCase()メソッドを識別する、バインドされていない非静的メソッド参照ですString。ただし、非静的メソッドには引き続きレシーバーオブジェクト(この例StringではtoLowerCase()、メソッド参照を介して呼び出すために使用されるオブジェクト)が必要なため、レシーバーオブジェクトは仮想マシンによって作成されます。toLowerCase()このオブジェクトで呼び出されます。レシーバーオブジェクトでString::toLowerCaseある単一のString引数を取り、String結果を返すメソッドを指定します。String::toLowerCase()ラムダと同等(String s) -> { return s.toLowerCase(); }です。

リスト3は、このバインドされていない非静的メソッド参照を示しています。

リスト3.MRDemo.java(バージョン3)

import java.util.function.Function; public class MRDemo { public static void main(String[] args) { print(String::toLowerCase, "STRING TO LOWERCASE"); print(s -> s.toLowerCase(), "STRING TO LOWERCASE"); print(new Function() { @Override public String apply(String s) // receives argument in parameter s; { // doesn't need to close over s return s.toLowerCase(); } }, "STRING TO LOWERCASE"); } public static void print(Function function, String s) { System.out.println(function.apply(s)); } }

Listing 3's main() method invokes the print() class method with functionality to convert a string to lowercase and the string to be converted as the method's arguments. print() is invoked in method reference (String::toLowerCase, where toLowerCase() isn't bound to a user-specified object) and equivalent lambda and anonymous class contexts.

I've defined print() to use the java.util.function.Function predefined functional interface, which represents a function that accepts one argument and produces a result. In this case, the Function instance passed to print() implements its R apply(T t) method to return s.toLowerCase(); print() outputs this string.

Although the String part of String::toLowerCase makes it look like a class is being referenced, only an instance of this class is referenced. The anonymous class example makes this more obvious. Note that in the anonymous class example the lambda receives an argument; it doesn't close over parameter s (i.e., it's not a closure).

Compile Listing 3 and run the application. You'll observe the following output:

string to lowercase string to lowercase string to lowercase

References to constructors

You can use a method reference to refer to a constructor without instantiating the named class. This kind of method reference is known as a constructor reference. Its syntax is className::new. className must support object creation; it cannot name an abstract class or interface. Keyword new names the referenced constructor. Here are some examples:

  • Character::new: equivalent to lambda (Character ch) -> new Character(ch)
  • Long::new: equivalent to lambda (long value) -> new Long(value) or (String s) -> new Long(s)
  • ArrayList::new: equivalent to lambda () -> new ArrayList()
  • float[]::new: equivalent to lambda (int size) -> new float[size]

The last constructor reference example specifies an array type instead of a class type, but the principle is the same. The example demonstrates an array constructor reference to the "constructor" of an array type.

To create a constructor reference, specify new without a constructor. When a class such as java.lang.Long declares multiple constructors, the compiler compares the functional interface's type against all of the constructors and chooses the best match. Listing 4 demonstrates a constructor reference.

Listing 4. MRDemo.java (version 4)

import java.util.function.Supplier; public class MRDemo { public static void main(String[] args) { Supplier supplier = MRDemo::new; System.out.println(supplier.get()); } }

Listing 4's MRDemo::new constructor reference is equivalent to lambda () -> new MRDemo(). Expression supplier.get() executes this lambda, which invokes MRDemo's default no-argument constructor and returns the MRDemo object, which is passed to System.out.println(). This method converts the object to a string, which it prints.

Now suppose you have a class with a no-argument constructor and a constructor that takes an argument, and you want to call the constructor that takes an argument. You can accomplish this task by choosing a different functional interface, such as the predefined Function interface shown in Listing 5.

Listing 5. MRDemo.java (version 5)

import java.util.function.Function; public class MRDemo { private String name; MRDemo() { name = ""; } MRDemo(String name) { this.name = name; System.out.printf("MRDemo(String name) called with %s%n", name); } public static void main(String[] args) { Function function = MRDemo::new; System.out.println(function.apply("some name")); } }

Function function = MRDemo::new;のメソッドは単一の(このコンテキストでは)引数を必要とするStringため、コンパイラは引数を取るコンストラクタを検索します。実行すると、に渡されます。Functionapply()Stringfunction.apply("some name")"some name"MRDemo(String name)