在 Java 8 中,如果 Optional
对象存在,我想对它做一些事情,如果它不存在,我想做另一件事。
if (opt.isPresent()) {
System.out.println("found");
} else {
System.out.println("Not found");
}
不过,这不是“功能风格”。
Optional
有一个 ifPresent()
方法,但我无法链接 orElse()
方法。
因此,我不能写:
opt.ifPresent( x -> System.out.println("found " + x))
.orElse( System.out.println("NOT FOUND"));
在回复@assylias 时,我认为 Optional.map()
不适用于以下情况:
opt.map( o -> {
System.out.println("while opt is present...");
o.setProperty(xxx);
dao.update(o);
return null;
}).orElseGet( () -> {
System.out.println("create new obj");
dao.save(new obj);
return null;
});
在这种情况下,当存在 opt
时,我会更新其属性并保存到数据库中。当它不可用时,我创建一个新的 obj
并保存到数据库中。
请注意,在两个 lambda 表达式中,我必须返回 null
。
但是当存在 opt
时,两个 lambdas 都将被执行。 obj
将被更新,并且一个新对象将被保存到数据库中。这是因为第一个 lambda 中的 return null
。 orElseGet()
将继续执行。
return null;
替换为 return o;
(两者)。但是,我有强烈的感觉,你在错误的地方工作。您应该在产生该 Optional
的站点工作。在那个地方应该有一种方法可以在没有中间 Optional
的情况下执行所需的操作。
ifPresent
与此相矛盾。所有其他方法都引用值而不是操作。
如果您使用的是 Java 9+,则可以使用 ifPresentOrElse()
方法:
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
对我来说@Dane White 的答案是好的,首先我不喜欢使用 Runnable
但我找不到任何替代品。
这里我更喜欢另一个实现:
public class OptionalConsumer<T> {
private Optional<T> optional;
private OptionalConsumer(Optional<T> optional) {
this.optional = optional;
}
public static <T> OptionalConsumer<T> of(Optional<T> optional) {
return new OptionalConsumer<>(optional);
}
public OptionalConsumer<T> ifPresent(Consumer<T> c) {
optional.ifPresent(c);
return this;
}
public OptionalConsumer<T> ifNotPresent(Runnable r) {
if (!optional.isPresent()) {
r.run();
}
return this;
}
}
然后:
Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s -> System.out.println("isPresent " + s))
.ifNotPresent(() -> System.out.println("! isPresent"));
更新1:
当您拥有价值并想要处理它时,上述针对传统开发方式的解决方案但是如果我想定义功能并执行将如何,请检查以下增强功能;
public class OptionalConsumer<T> implements Consumer<Optional<T>> {
private final Consumer<T> c;
private final Runnable r;
public OptionalConsumer(Consumer<T> c, Runnable r) {
super();
this.c = c;
this.r = r;
}
public static <T> OptionalConsumer<T> of(Consumer<T> c, Runnable r) {
return new OptionalConsumer(c, r);
}
@Override
public void accept(Optional<T> t) {
if (t.isPresent()) {
c.accept(t.get());
}
else {
r.run();
}
}
然后可以用作:
Consumer<Optional<Integer>> c = OptionalConsumer.of(
System.out::println,
() -> System.out.println("Not fit")
);
IntStream.range(0, 100)
.boxed()
.map(i -> Optional.of(i)
.filter(j -> j % 2 == 0))
.forEach(c);
在这个新代码中,你有 3 件事:
可以在对象存在之前轻松定义功能。没有为每个 Optional 创建对象引用,只有一个,你的内存比 less GC 少。它正在实现消费者以便更好地与其他组件一起使用。
顺便说一句,现在它的名称更具描述性,它实际上是 Consumer
Java 9 介绍
ifPresentOrElse 如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。
见优秀的Optional in Java 8 cheat sheet。
它为大多数用例提供了所有答案。
下面的简短摘要
ifPresent() - 设置 Optional 时做一些事情
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
filter() - 拒绝(过滤掉)某些可选值。
opt.filter(x -> x.contains("ab")).ifPresent(this::print);
map() - 转换值(如果存在)
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
orElse()/orElseGet() - 转为空 可选为默认 T
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
orElseThrow() - 懒惰地在空时抛出异常 Optional
opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
map
返回 null,他可以让它工作,但要求一个功能性解决方案有点奇怪,这样你就可以调用 DAO。在我看来,从这个 map.orElse
块返回更新/新对象然后对返回的对象做你需要做的事情会更有意义。
map
专注于流本身,而不是“根据流中此元素的状态对另一个对象执行操作”。很高兴知道在 Java 9 中添加了 ifPresentOrElse
。
另一种选择是:
System.out.println(opt.map(o -> "Found")
.orElse("Not found"));
我认为它不会提高可读性。
或者按照 Marko 的建议,使用三元运算符:
System.out.println(opt.isPresent() ? "Found" : "Not found");
new Object();
但老实说这变得非常难看。对于您更新的示例,我会坚持使用 if/else。
map
只返回 Optional
进行链接会使代码更难理解,而 map
被假定为逐字映射到某物。
另一种解决方案是使用高阶函数,如下所示
opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
.orElse(() -> System.out.println("Not Found"))
.run();
value -> () -> syso
部分的含义。
String result = opt.map(value -> "withOptional").orElse("without optional");
没有一种开箱即用的好方法。如果您想定期使用更简洁的语法,那么您可以创建一个实用程序类来提供帮助:
public class OptionalEx {
private boolean isPresent;
private OptionalEx(boolean isPresent) {
this.isPresent = isPresent;
}
public void orElse(Runnable runner) {
if (!isPresent) {
runner.run();
}
}
public static <T> OptionalEx ifPresent(Optional<T> opt, Consumer<? super T> consumer) {
if (opt.isPresent()) {
consumer.accept(opt.get());
return new OptionalEx(true);
}
return new OptionalEx(false);
}
}
然后你可以在别处使用静态导入来获得接近你所追求的语法:
import static com.example.OptionalEx.ifPresent;
ifPresent(opt, x -> System.out.println("found " + x))
.orElse(() -> System.out.println("NOT FOUND"));
Optional.ifPresentOrElse()
已添加到 JDK 9。
如果您只能使用 Java 8 或更低版本:
1)如果您没有spring-data
,那么到目前为止最好的方法是:
opt.<Runnable>map(param -> () -> System.out.println(param))
.orElse(() -> System.out.println("no-param-specified"))
.run();
现在我知道它对某人来说不是那么易读甚至难以理解,但对我个人来说看起来不错,而且我没有看到另一种很好的流畅方式。
2) 如果您足够幸运并且可以使用 spring-data
,那么最好的方法是 Optionals#ifPresentOrElse:
Optionals.ifPresentOrElse(opt, System.out::println,
() -> System.out.println("no-param-specified"));
如果您可以使用 Java 9,则绝对应该使用:
opt.ifPresentOrElse(System.out::println,
() -> System.out.println("no-param-specified"));
您不能在 ifPresent
之后调用 orElse
,原因是 orElse
是在可选参数上调用的,但 ifPresent
返回 void。所以最好的实现方法是ifPresentOrElse
。它可能是这样的:
op.ifPresentOrElse(
(value)
-> { System.out.println(
"Value is present, its: "
+ value); },
()
-> { System.out.println(
"Value is empty"); });
所描述的行为可以通过使用 Vavr(以前称为 Javaslang)来实现,这是一个用于 Java 8+ 的对象函数库,它实现了大多数 Scala 构造(Scala 是一种更具表现力的语言,具有基于 JVM 构建的更丰富的类型系统)。它是一个非常好的库,可以添加到您的 Java 项目中以编写纯函数式代码。
Vavr 提供了 Option
monad,它提供了使用 Option 类型的函数,例如:
fold:在两种情况下映射选项的值(定义/空)
onEmpty:允许在选项为空时执行 Runnable
peek:允许使用选项的值(定义时)。
与 Optional 相反,它也是可序列化的,这意味着您可以安全地将其用作方法参数和实例成员。
Option 遵循与 Java 的 Optional “伪单子”不同的单子法则,并提供更丰富的 API。当然,您可以从 Java 的 Optional 中实现(反之亦然):Option.ofOptional(javaOptional)
–Vavr 专注于互操作性。
转到示例:
// AWESOME Vavr functional collections (immutable for the gread good :)
// fully convertible to Java's counterparts.
final Map<String, String> map = Map("key1", "value1", "key2", "value2");
final Option<String> opt = map.get("nonExistentKey"); // you're safe of null refs!
final String result = opt.fold(
() -> "Not found!!!", // Option is None
val -> "Found the value: " + val // Option is Some(val)
);
此外,所有 Vavr 类型都可以转换为其 Java 对应类型,例如:Optional javaOptional = opt.toJava()
,非常简单:) 当然,转换也存在于其他方式:Option option = Option.ofOptional(javaOptional)
。
NB Vavr 提供了一个带有许多方便的静态方法的 io.vavr.API
类 =)
进一步阅读
Null reference, the billion dollar mistake
NB 这只是 Vavr 提供的一个非常小的例子(模式匹配、流又名惰性评估列表、一元类型、不可变集合......)。
这里的问题:
optional
.map(object -> {
System.out.println("If present.");
return null;
})
.orElseGet( () -> {
System.out.println("If empty.");
return null;
});
是不是 map()
将第一个函数返回的 null
转换为 empty()
;然后它返回 empty()
。当它返回 empty()
时,它会提示调用第二个函数。请注意,orElseGet()
不会将第二个函数返回的 null
转换为 empty()
,因此它将返回 null
。
查看 map()
的实现:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
以及 orElseGet()
的实现:
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
因此在执行时:
如果 optional.isPresent(),系统将打印 If present., then If empty.,并且表达式的计算结果为 null。
如果 !optional.isPresent(),系统将打印 If empty.,并且表达式将评估为 null。
如果提供给 map()
的函数返回 any 其他值 - any 其他值 - 代码将按您的预期工作,提供给 map()
的函数在以下情况下被执行isPresent()
以及如果 !isPresent()
提供给 orElseGet()
的函数:
例如,这个:
optional
.map(data -> {
System.out.println("If present.");
return 0;
})
.orElseGet( () -> {
System.out.println("If empty.");
return 0;
});
执行时:
如果 optional.isPresent(),系统将打印 If present.,并且表达式的计算结果为 0。
if !optional.isPresent(),系统将打印 If empty.,并且表达式的计算结果为 0。
如果您的具体情况,我建议您的 insert
和 update
方法返回,例如,持久对象,或持久对象的 id,或类似有用的东西;那么你可以使用类似这样的代码:
final Object persist = optional
.map(object -> {
System.out.println("If present.");
return update(object);
})
.orElseGet( () -> {
System.out.println("If empty.");
return insert(new Object());
});
另一种解决方案可能如下:
这是你如何使用它:
final Opt<String> opt = Opt.of("I'm a cool text");
opt.ifPresent()
.apply(s -> System.out.printf("Text is: %s\n", s))
.elseApply(() -> System.out.println("no text available"));
或者,如果您遇到相反的用例,则为真:
final Opt<String> opt = Opt.of("This is the text");
opt.ifNotPresent()
.apply(() -> System.out.println("Not present"))
.elseApply(t -> /*do something here*/);
这是成分:
小修改 Function 接口,仅用于“elseApply”方法 可选增强 一点点curring :-)
“美容”增强的功能界面。
@FunctionalInterface
public interface Fkt<T, R> extends Function<T, R> {
default R elseApply(final T t) {
return this.apply(t);
}
}
以及用于增强的可选包装类:
public class Opt<T> {
private final Optional<T> optional;
private Opt(final Optional<T> theOptional) {
this.optional = theOptional;
}
public static <T> Opt<T> of(final T value) {
return new Opt<>(Optional.of(value));
}
public static <T> Opt<T> of(final Optional<T> optional) {
return new Opt<>(optional);
}
public static <T> Opt<T> ofNullable(final T value) {
return new Opt<>(Optional.ofNullable(value));
}
public static <T> Opt<T> empty() {
return new Opt<>(Optional.empty());
}
private final BiFunction<Consumer<T>, Runnable, Void> ifPresent = (present, notPresent) -> {
if (this.optional.isPresent()) {
present.accept(this.optional.get());
} else {
notPresent.run();
}
return null;
};
private final BiFunction<Runnable, Consumer<T>, Void> ifNotPresent = (notPresent, present) -> {
if (!this.optional.isPresent()) {
notPresent.run();
} else {
present.accept(this.optional.get());
}
return null;
};
public Fkt<Consumer<T>, Fkt<Runnable, Void>> ifPresent() {
return Opt.curry(this.ifPresent);
}
public Fkt<Runnable, Fkt<Consumer<T>, Void>> ifNotPresent() {
return Opt.curry(this.ifNotPresent);
}
private static <X, Y, Z> Fkt<X, Fkt<Y, Z>> curry(final BiFunction<X, Y, Z> function) {
return (final X x) -> (final Y y) -> function.apply(x, y);
}
}
这应该可以解决问题,并且可以作为如何处理此类要求的基本模板。
这里的基本思想如下。在非函数式编程世界中,您可能会实现一个采用两个参数的方法,其中第一个是一种可运行代码,如果值可用,则应执行该代码,另一个参数是可运行代码,以防万一值不可用。为了更好的可读性,您可以使用curring将两个参数的函数拆分为两个各一个参数的函数。这就是我在这里基本上所做的。
提示:Opt 还提供了另一个用例,您希望在该用例中执行一段代码,以防该值不可用。这也可以通过 Optional.filter.stuff 来完成,但我发现这更具可读性。
希望有帮助!
附加信息:
还有另一种使用柯里化来表达“if then else”的方法:
public static <X, Y> Function<Predicate<X>, Function<Function<X, Y>, Function<Function<X, Y>, Y>>> ifThenElse(X input) {
return (final Predicate<X> pred) -> (final Function<X, Y> ifPresent) -> (final Function<X, Y> ifNotPresent) -> pred.test(input) ? ifPresent.apply(input) : ifNotPresent.apply(input);
}
这样就可以说:
final String result = ifThenElse("fancy")
.apply(input -> input.contains("fancy")) /* test */
.apply(input -> input.toUpperCase()) /* if-case */
.apply(input -> input.toLowerCase()); /* else-case */
如果您想存储值:
Pair.of<List<>, List<>> output = opt.map(details -> Pair.of(details.a, details.b))).orElseGet(() -> Pair.of(Collections.emptyList(), Collections.emptyList()));
假设您有一个列表并避免了 isPresent()
问题(与可选项相关),您可以使用 .iterator().hasNext()
检查是否不存在。