Optional<T>.orElse()
メソッドと Optional<T>.orElseGet()
メソッドの違いを理解しようとしています。
orElse()
メソッドの説明は、 "存在する場合は値を返し、それ以外の場合はotherを返します。"
一方、orElseGet()
メソッドの説明はです。「存在する場合は値を返し、それ以外の場合はotherを呼び出してその呼び出しの結果を返します。」
orElseGet()
メソッドはSupplier関数型インタフェースを取ります。これは基本的にパラメータを一切取りません。そしてT
を返します。
どの状況でorElseGet()
を使う必要がありますか?もしあなたがT myDefault()
というメソッドを持っているなら、なぜあなたはoptional.orElse(myDefault())
ではなくoptional.orElseGet(() -> myDefault())
をやらないのですか?
orElseGet()
がラムダ式の実行をしばらく後に延期しているようには見えませんが、そのポイントは何ですか? (get()
が決してNoSuchElementException
をスローせず、isPresent()
が常にtrueを返さないより安全なOptional<T>
を返した方がより有用であると思いました。しかし明らかにそうではありません、orElse()
のようにT
を返すだけです).
私が見逃している他のいくつかの違いはありますか?
これら二つのシナリオを取りなさい:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
opt
に値が含まれていない場合、この2つはまったく同じです。しかし、opt
にに値が含まれている場合、Foo
オブジェクトはいくつ作成されるのでしょうか。
Ps:もちろんこの例では違いは測定できないでしょうが、たとえばリモートWebサービスやデータベースからデフォルト値を取得する必要がある場合、それは突然非常に重要になります
短い答え:
Optional.isPresent()
の値に関係なく、必要に応じて指定された関数を常に呼び出します。Optional.isPresent() == false
が指定された場合にのみ、指定された関数を呼び出します。実際のコードでは、必要なリソースが取得にコストがかかる場合に、2番目のアプローチを検討することをお勧めします。
// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());
// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())
詳細については、この関数を使った次の例を検討してください。
public Optional<String> findMyPhone(int phoneId)
違いは以下のとおりです。
X : buyNewExpensivePhone() called
+——————————————————————————————————————————————————————————————————+——————————————+
| Optional.isPresent() | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+——————————————————————————————————————————————————————————————————+——————————————+
optional.isPresent() == false
の場合、2つの方法に違いはありません。ただし、optional.isPresent() == true
の場合、orElse()
は、必要に応じて後続の機能を常に呼び出します。
最後に、使用したテストケースは以下のとおりです。
結果:
------------- Scenario 1 - orElse() --------------------
1.1. Optional.isPresent() == true
Going to a very far store to buy a new expensive phone
Used phone: MyCheapPhone
1.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone
------------- Scenario 2 - orElseGet() --------------------
2.1. Optional.isPresent() == true
Used phone: MyCheapPhone
2.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone
コード:
public class TestOptional {
public Optional<String> findMyPhone(int phoneId) {
return phoneId == 10
? Optional.of("MyCheapPhone")
: Optional.empty();
}
public String buyNewExpensivePhone() {
System.out.println("\tGoing to a very far store to buy a new expensive phone");
return "NewExpensivePhone";
}
public static void main(String[] args) {
TestOptional test = new TestOptional();
String phone;
System.out.println("------------- Scenario 1 - orElse() --------------------");
System.out.println(" 1.1. Optional.isPresent() == true");
phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println(" 1.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println("------------- Scenario 2 - orElseGet() --------------------");
System.out.println(" 2.1. Optional.isPresent() == true");
// Can be written as test::buyNewExpensivePhone
phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println(" 2.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
}
}
ここにたどり着きました 工藤 言及しました。
自分の経験を他の人にも伝えています。
orElse
、またはorElseGet
です。static String B() {
System.out.println("B()...");
return "B";
}
public static void main(final String... args) {
System.out.println(Optional.of("A").orElse(B()));
System.out.println(Optional.of("A").orElseGet(() -> B()));
}
版画
B()...
A
A
orElse
とorElseGet
の最大の違いは、else
条件で新しい値を取得するために何かを評価したいときに生じると思います。
この単純な例を考えます -
// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
value = oldValue;
} else {
value = apicall().value;
}
それでは、上記の例をOptional
とorElse
の併用に変換しましょう。
// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);
それでは、上記の例をOptional
とorElseGet
の併用に変換しましょう。
// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);
orElse
が呼び出されると、apicall().value
が評価されてメソッドに渡されます。一方、orElseGet
の場合、評価はoldValue
が空の場合にのみ行われます。 orElseGet
は遅延評価を可能にします。
次の例で違いを説明します。
String destroyTheWorld() {
// destroy the world logic
return "successfully destroyed the world";
}
Optional<String> opt = Optional.empty();
// we're dead
opt.orElse(destroyTheWorld());
// we're safe
opt.orElseGet(() -> destroyTheWorld());
答えはドキュメントにも表示されます。
public T orElseGet(Supplier<? extends T> other)
:
存在する場合は値を返し、存在しない場合はotherを呼び出し、その呼び出しの結果を返します。
Supplier
は、Optional
が存在する場合は呼び出されません。一方、
存在すれば値を返し、そうでなければotherを返します。
other
が文字列を返すメソッドの場合、それは呼び出されますが、Optional
が存在する場合はその値は返されません。
違いはかなり微妙であり、あまり注意を払わないと、間違った方法で使用し続けることになります。
orElse()
とorElseGet()
の違いを理解する最良の方法は、Optional<T>
がnullまたはnotの場合にorElse()
が常に実行されることですが、orElseGet()
は次の場合にのみ実行されますOptional<T>
はnullです。
orElseの辞書の意味は:-です。何かが存在しない場合にパーツを実行しますが、ここでは矛盾します。以下の例を参照してください。
Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
String value = nonEmptyOptional.orElse(iAmStillExecuted());
public static String iAmStillExecuted(){
System.out.println("nonEmptyOptional is not NULL,still I am being executed");
return "I got executed";
}
出力: nonEmptyOptionalはNULLではなく、まだ実行されています
Optional<String> emptyOptional = Optional.ofNullable(null);
String value = emptyOptional.orElse(iAmStillExecuted());
public static String iAmStillExecuted(){
System.out.println("emptyOptional is NULL, I am being executed, it is normal as
per dictionary");
return "I got executed";
}
出力:emptyOptionalはNULL、実行されています、辞書によると正常です
orElseGet()
の場合、メソッドはディクショナリの意味に従います。orElseGet()
部分は、Optionalがnullの場合にのみ実行されます。
ベンチマーク:
+--------------------+------+-----+------------+-------------+-------+
| Benchmark | Mode | Cnt | Score | Error | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark | avgt | 20 | 60934.425 | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20 | 3.798 | ± 0.030 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
備考:
orElseGet()
は、特定の例ではorElse()
より明らかに優れています。
それは非常に基本的な地面の例を望んでいる私のような人々の疑念をクリアすることを願っています:)
次のコードを考慮してください:
import Java.util.Optional;
// one class needs to have a main() method
public class Test
{
public String orelesMethod() {
System.out.println("in the Method");
return "hello";
}
public void test() {
String value;
value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
System.out.println(value);
value = Optional.<String>ofNullable("test").orElse(orelesMethod());
System.out.println(value);
}
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
Test test = new Test();
test.test();
}
}
value
を次のように取得する場合:Optional.<String>ofNullable(null)
、orElseGet()とorElse()に違いはありませんが、value
を次のように取得する場合:Optional.<String>ofNullable("test")
、 orelesMethod()
のorElseGet()
は呼び出されませんが、orElse()
では呼び出されます