在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
并保存到数据库。
注意在两个lambdas我必须返回null
。
但是当opt
出现时,两个lambdas都会被执行。将更新obj
,并将新对象保存到数据库中。这是因为第一个lambda中的return null
。 orElseGet()
将继续执行。
对我来说,@ 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);
在这个新代码中,您有三件事:
顺便说一句,现在它的名字更具描述性,实际上是消费者>
如果你想存储价值:
qazxswpoi
假设您有一个列表并且避免了isPresent()问题(与选项相关),您可以使用.iterator()。hasNext()来检查是否存在。
如果您使用的是Java 9,则可以使用ifPresentOrElse()
方法:
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
看到优秀的Optional in Java 8 cheat sheet。
它为大多数用例提供了所有答案。
以下简短摘要
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
opt.filter(x -> x.contains("ab")).ifPresent(this::print);
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
另一种选择是:
System.out.println(opt.map(o -> "Found")
.orElse("Not found"));
我不认为这会提高可读性。
或者正如Marko建议的那样,使用三元运算符:
System.out.println(opt.isPresent() ? "Found" : "Not found");
另一种解决方案是使用如下的高阶函数
opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
.orElse(() -> System.out.println("Not Found"))
.run();
没有很好的方法可以开箱即用。如果您希望定期使用更清晰的语法,那么您可以创建一个实用程序类来帮助:
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"));
如果只能使用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"));
另一个解决方案可能是:
这是你如何使用它:
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*/);
这是成分:
“美容”增强功能界面。
@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完成,但我发现这更具可读性。
希望有所帮助!
好编程:-)
所描述的行为可以通过使用Vavr(以前称为Javaslang)来实现,Option
是Java 8+的对象功能库,它实现了大多数Scala构造(Scala是一种更具表现力的语言,具有在JVM上构建的更丰富的类型系统)。这是一个非常好的库,可以添加到Java项目中以编写纯函数代码。
Vavr提供了fold
monad,它提供了使用Option类型的函数,例如:
onEmpty
:映射两种情况下的选项值(已定义/空)Runnable
:允许在选项为空时执行peek
Option.ofOptional(javaOptional)
:允许使用选项的值(定义时)。选项遵循与Java的Optional“伪monad”不同的monad定律,并提供更丰富的API。当然,您可以使用Java的Optional(以及相反的方式)来实现它:// 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专注于互操作性。
Null reference, the billion dollar mistake
Pair.of<List<>, List<>> output = opt.map(details -> Pair.of(details.a, details.b))).orElseGet(() -> Pair.of(Collections.emptyList(), Collections.emptyList()));
注:这只是Vavr提供的一个非常小的例子(模式匹配,流a.k.a.懒惰评估列表,monadic类型,不可变集合,......)。