このコードの違いは何ですか?
Supplier<LocalDate> s1 = LocalDate::now;
LocalDate s2 = LocalDate.now();
System.out.println(s1.get()); //2016-10-25
System.out.println(s2); //2016-10-25
私はJava 8で機能インターフェースの学習を開始し、サプライヤーのメリットを理解していません。いつ、どのように、正確に、それらを使用する必要があります。サプライヤーは、パフォーマンスを向上させるか、または抽象化レベルでのメリットを実現しますか? ?
ご回答ありがとうございます!また、検索を使用して必要なものが見つからなかったため、重複した質問ではありません。
更新1:これはどういう意味ですか?
Supplier<Long> s1 = System::currentTimeMillis;
Long s2 = System.currentTimeMillis();
System.out.println(s1.get()); //1477411877817
System.out.println(s2); //1477411877817
try {
Thread.sleep(3000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s1.get()); //1477411880817 - different
System.out.println(s2); //1477411877817
確かにパフォーマンスは向上しません。あなたの質問はこれと似ています:なぜ変数を使用しているのですか?必要になるたびにすべてを再計算するだけで済みます。正しい?
メソッドを何度も使用する必要があるが、構文が複雑な場合。
MyAmazingClass
という名前のクラスがあり、MyEvenBetterMethod
(静的)という名前のメソッドがあり、15の異なる位置で15回呼び出す必要があるとします。コード。もちろん、次のようなことができます...
int myVar = MyAmazingClass.MyEvenBetterMethod();
// ...
int myOtherVar = MyAmazingClass.MyEvenBetterMethod();
// And so on...
...しかし、あなたも行うことができます
Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod;
int myVar = shorter.get();
// ...
int myOtherVar = shorter.get();
// And so on...
LocalDate
ではなく_Supplier<LocalDate>
_を使用する必要があるシナリオを説明します。
LocalDate.now()
などの静的メソッドを直接呼び出すコードは、単体テストが非常に困難です。人の年齢を計算するメソッドgetAge()
を単体テストするシナリオを考えてみましょう。
_class Person {
final String name;
private final LocalDate dateOfBirth;
Person(String name, LocalDate dateOfBirth) {
this.name = name;
this.dateOfBirth = dateOfBirth;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now());
}
}
_
これは本番環境では問題なく機能します。しかし、ユニットテストでは、システムの日付を既知の値に設定するか、毎年更新して、返される年齢が1ずつ増えることを期待する必要があります。どちらもかなり素晴らしいソリューションです。
より良い解決策は、量産コードでLocalDate.now()
を使用できるようにしながら、ユニットテストで既知の日付を挿入することです。多分このようなもの:
_class Person {
final String name;
private final LocalDate dateOfBirth;
private final LocalDate currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, LocalDate currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate);
}
}
_
オブジェクトが作成されてから人物の誕生日が過ぎたシナリオを考えてみましょう。この実装では、getAge()
は、現在の日付ではなく、Personオブジェクトが作成された日時に基づきます。 _Supplier<LocalDate>
_を使用してこれを解決できます。
_class Person {
final String name;
private final LocalDate dateOfBirth;
private final Supplier<LocalDate> currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, ()-> LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, Supplier<LocalDate> currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate.get());
}
public static void main(String... args) throws InterruptedException {
// current date 2016-02-11
Person person = new Person("John Doe", LocalDate.parse("2010-02-12"));
printAge(person);
TimeUnit.DAYS.sleep(1);
printAge(person);
}
private static void printAge(Person person) {
System.out.println(person.name + " is " + person.getAge());
}
}
_
出力は正しくなります:
_John Doe is 5
John Doe is 6
_
ユニットテストでは、次のように「今」の日付を挿入できます。
_@Test
void testGetAge() {
Supplier<LocalDate> injectedNow = ()-> LocalDate.parse("2016-12-01");
Person person = new Person("John Doe", LocalDate.parse("2004-12-01"), injectedNow);
assertEquals(12, person.getAge());
}
_
関数型インターフェースとメソッド参照を混乱させています。 Supplier
はCallable
に似た単なるインターフェースであり、Java 5以降で知っておく必要があります。唯一の違いは、Callable.call
がチェック済みException
sをスローできることです。 Supplier.get
とは異なります。したがって、これらのインターフェースは同様のユースケースになります。
さて、これらのインターフェースは偶然にもfunctional interfacesです。これは、それらがメソッド参照として実装され、インターフェースメソッドが呼び出されたときに呼び出される既存のメソッドを指すことができることを意味します。
つまり、Java 8より前は、
Future<Double> f=executorService.submit(new Callable<Double>() {
public Double call() throws Exception {
return calculatePI();
}
});
/* do some other work */
Double result=f.get();
そして今、あなたは書くことができます
Future<Double> f=executorService.submit(() -> calculatePI());
/* do some other work */
Double result=f.get();
または
Future<Double> f=executorService.submit(MyClass::calculatePI);
/* do some other work */
Double result=f.get();
質問使用する場合Callable
はまったく変わっていません。
同様に、Supplier
をいつ使用するかという問題は、それを実装する方法ではなく、どのAPIを使用するかによって異なります。
CompletableFuture<Double> f=CompletableFuture.supplyAsync(MyClass::calculatePI);
/* do some other work */
Double result=f.join();// unlike Future.get, no checked exception to handle...
私はあなたの質問がすでに提供された答えによって答えられたと確信しています。これはサプライヤーについての私の理解です。
これは、デフォルトのJava定義されたインターフェースで、ラムダメソッドの参照ターゲットの戻り時間のラッパーとして機能します。引数を持たず、オブジェクトを返すメソッドを記述した場所に、サプライヤーインターフェースによってラップされます。それがなければ、プレジェネリックでは、戻り値をオブジェクトクラスとしてキャプチャしてキャストし、ジェネリックでは独自のT(ype)を定義して、戻り値:get()を呼び出して、上記の引数なしのメソッドから返されたオブジェクトを抽出できる、統一された戻り型の汎用ホルダーを提供するだけです。
私は答えに満足していないので、私の見解を追加します。サプライヤは、実行を遅らせたい場合に役立ちます。
サプライヤーなし
config.getLocalValue(getFromInternet() /*value if absent*/);
GetLocalValueが呼び出される前にgetFromInternetが呼び出されますが、getFromInternet()の値はローカル値が存在しない場合にのみ使用されます。
さて、config.getLocalValue
サプライヤーを受け入れることができます。この実行を遅らせることができます。さらに、ローカル値が存在する場合は実行しません。
config.getLocalValue(() -> getFromInternet())
違いサプライヤーはそれを可能にします:execute only when and if needed
Effective Java by Joshua Bloch のアイテム5の説明は、これを私に明確にしました:
Java 8で導入されたサプライヤーインターフェースは、ファクトリーを表すのに最適です。サプライヤーを入力に使用するメソッドは、通常、境界ワイルドカードタイプ(アイテム31)を使用してファクトリのタイプパラメーターを制約し、指定したタイプのサブタイプを作成するファクトリを渡すクライアント。たとえば、クライアントが提供するファクトリを使用してモザイクを作成し、各タイルを生成するメソッドは次のとおりです。
Mosaic create(Supplier<? extends Tile> tileFactory) { ... }
リソースファクトリを使用してオブジェクトを作成するオブジェクトに、リソースファクトリを送信することができます。ここで、継承ツリーのさまざまな位置にオブジェクトを生成する各リソースファクトリでさまざまなリソースファクトリを送信したいとしますが、リソースファクトリを受け取るオブジェクトは、どのような場合でもそれらを受け取ることができる必要があります。
次に、バインドされたワイルドカードを使用して、特定のクラス/インターフェースを拡張/実装するオブジェクトを返すメソッドを受け入れるサプライヤーを実装し、そのサプライヤーをリソースファクトリとして使用できます。
本の項目5を読むと、使い方を完全に理解するのに役立つ場合があります。