我想了解Optional<T>.orElse()
和Optional<T>.orElseGet()
方法之间的区别。
orElse()
方法的描述是“如果存在则返回值,否则返回其他值。”
虽然,orElseGet()
方法的描述是“如果存在则返回值,否则调用other并返回该调用的结果。”
orElseGet()
方法采用供应商功能接口,基本上不接受任何参数并返回T
。
您需要在哪种情况下使用orElseGet()
?如果您有方法T myDefault()
,为什么不能optional.orElse(myDefault())
而不是optional.orElseGet(() -> myDefault())
?
orElseGet()
似乎没有将lambda表达式的执行推迟到以后的某个时间或什么的,所以有什么意义呢? (我本以为如果它返回一个更安全的Optional<T>
会更有用,get()
永远不会抛出NoSuchElementException
而isPresent()
总是返回true ...但显然它不是,它只返回T
之类的orElse()
。
我还缺少其他一些差异吗?
答案 0 :(得分:135)
采取以下两种情况:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
如果opt
没有包含值,则两者确实相同。但是,如果opt
包含值,那么将创建多少Foo
个对象?
Ps:当然在这个例子中,差异可能无法衡量,但如果您必须从远程Web服务或数据库中获取默认值,它会突然变得非常重要的。
答案 1 :(得分:57)
简答:
Optional.isPresent()
值Optional.isPresent() == false
在实际代码中,当所需资源昂贵时时,您可能需要考虑第二种方法。
// 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
时,两种方式之间没有区别。但是,当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");
}
}
答案 2 :(得分:54)
我在这里找到了Kudo提到的问题。
我与他人分享我的经验。
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
答案 3 :(得分:34)
我想说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
允许延迟评估。
答案 4 :(得分:3)
区别非常细微,如果您不注意的话,则会以错误的方式使用它。
了解orElse()
和orElseGet()
之间区别的最好方法是,如果orElse()
为 null 或< strong>不是,但是Optional<T>
仅在orElseGet()
为空时执行。
orElse 的词典含义是:-在不存在某些内容但与之矛盾的情况下执行该部分,请参见以下示例:
Optional<T>
输出:nonEmptyOptional不为NULL,仍在执行中
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";
}
输出:empty可选为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"; }
,该方法按照字典的含义进行操作,orElseGet()
部分仅在Optional为 空。
基准:
orElseGet()
备注:对于我们的特定示例,
+--------------------+------+-----+------------+-------------+-------+ | 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()
。
希望它消除了像我这样想要基本示例的人们的疑虑:)
答案 5 :(得分:2)
首先检查这两种方法的声明。
1)OrElse:执行逻辑并将结果作为参数传递。
public T orElse(T other) {
return value != null ? value : other;
}
2)OrElseGet:如果可选字段中的值为null,则执行逻辑
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
有关上述声明的一些解释: 无论可选对象中的值是什么(空,空或带值),始终会执行“ Optional.orElse”的参数。使用“ Optional.orElse”时请始终牢记上述几点,否则在以下情况下使用“ Optional.orElse”可能会带来很大的风险。
风险-1)日志记录问题:如果orElse中的内容包含任何日志语句: 在这种情况下,您每次都将最终记录它。
Optional.of(getModel())
.map(x -> {
//some logic
})
.orElse(getDefaultAndLogError());
getDefaultAndLogError() {
log.error("No Data found, Returning default");
return defaultValue;
}
风险2)性能问题::如果orElse中的内容耗时,请执行以下操作: 耗时的内容可以是任何I / O操作数据库调用,API调用,文件读取。如果将此类内容放入orElse(),系统将最终执行无用的代码。
Optional.of(getModel())
.map(x -> //some logic)
.orElse(getDefaultFromDb());
getDefaultFromDb() {
return dataBaseServe.getDefaultValue(); //api call, db call.
}
风险3)非法状态或错误问题:如果orElse中的内容正在改变某些对象状态,请执行以下操作: 我们可能在Optional.map函数中说的另一个地方使用同一对象,这可能会使我们陷入严重的错误。
List<Model> list = new ArrayList<>();
Optional.of(getModel())
.map(x -> {
})
.orElse(get(list));
get(List < String > list) {
log.error("No Data found, Returning default");
list.add(defaultValue);
return defaultValue;
}
然后,什么时候可以使用orElse()? 当默认值是某个常量对象enum时,最好使用orElse。在上述所有情况下,我们都可以使用Optional.orElseGet()(仅当Optional包含非空值时执行),而不是Optional.orElse()。为什么??在orElse中,我们传递默认结果值,但在orElseGet中,我们传递Supplier,并且Supplier的方法仅在Optional中的值为null时才执行。
主要收获如下:
我已经在我的中等博客的第2点(“ Optional.map/Optional.orElse”!=“ if / else” )中对此进行了解释。 Use Java8 as a programmer not as a coder
答案 6 :(得分:1)
以下示例应说明差异:
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)
:
返回该值(如果存在),否则调用,然后返回 调用的结果。
如果出现Supplier
,则不会调用Optional
。而
返回该值(如果存在),否则返回其他。
如果other
是一个返回字符串的方法,则将调用它,但是如果Optional
存在,则不会返回其值。
答案 7 :(得分:0)
考虑以下代码:
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
:{{1 }},Optional.<String>ofNullable("test")
中的orelesMethod()
将不会被调用,但在orElseGet()
中它将被称为