Java 8의 Optional.ifPresent와 if-Not-Present의 기능 스타일?
자바 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
데이터베이스에 저장하십시오.
내가 반환해야 할 두 람다에 있는 메모null
.
하지만 언제opt
현재, 두 람다 모두 처형될 것이다.obj
업데이트될 것이고, 새로운 개체가 데이터베이스에 저장될 것이다. 때문이다.return null
첫 람다에그리고orElseGet()
계속 실행될 것이다.
Java 9+를 사용하는 경우 다음 방법을 사용하십시오.
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
나에게 @Dane White의 대답은 OK, 우선 나는 사용하는 것을 좋아하지 않았다.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);
이 새로운 코드에는 다음 세 가지가 있다.
- 쉽게 객체의 기존 기능을 정의할 수 있다.
- 각 옵션(옵션)에 대한 개체 참조를 만들지 않고, 하나만 생성하며, GC보다 메모리가 더 적은 경우.
- 그것은 다른 요소들과 함께 더 나은 사용을 위해 소비자를 구현하고 있다.
그런데, 이제 그 이름이 더 설명적이게 되었다. 그것은 실제로 소비자(Consumer)[Option?>>
Java 9가 소개
값이 있는 경우 PresentOrElse는 해당 값으로 지정된 작업을 수행하고, 그렇지 않은 경우 지정된 빈 기반 작업을 수행하십시오.
대부분의 사용 사례에 대한 모든 답변을 제공한다.
아래 간단한 요약
ifPresent() - 선택사항이 설정된 경우 작업 수행
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
필터() - 특정 선택적 값을 제거(필터링)
opt.filter(x -> x.contains("ab")).ifPresent(this::print);
mapprove - 변환 값(있는 경우)
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
orElse()/또는 ElseGet() - 비어 있는 항목을 기본 T로 설정
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
orElseDrow() - 비어 있는 옵션에서 느릿느릿 예외 발생
opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
대안은 다음과 같다.
System.out.println(opt.map(o -> "Found")
.orElse("Not found"));
하지만 나는 그것이 읽기 쉽게 개선된다고 생각하지 않는다.
또는 Marko가 제안한 대로 ternary 연산자를 사용하십시오.
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"));
전화할 수 없다.orElse
다음에ifPresent
orElse
안절부절 못하지만ifPresent
무효 반환그래서 성취하기 위한 가장 좋은 접근법이다.ifPresentOrElse
될 도 있다 이렇게 될 수도 있다.
op.ifPresentOrElse(
(value)
-> { System.out.println(
"Value is present, its: "
+ value); },
()
-> { System.out.println(
"Value is empty"); });
기술된 행동은 대부분의 스칼라 구조를 구현하는 자바 8+의 객체 기능 라이브러리인 Vavr(이전의 자바스랑으로 알려져 있음)를 사용하여 달성할 수 있다(Scala는 JVM에 구축된 보다 풍부한 유형 시스템을 가진 표현 언어).순수 기능 코드를 작성하기 위해 Java 프로젝트에 추가하는 것은 매우 좋은 라이브러리 입니다.
Vavr이 제공하는 것은Option
다음과 같은 옵션 유형과 함께 작동하는 기능을 제공하는 모나드:
fold
두 에 두 경우 모두에 대해 옵션 값을 매핑하려면(정의/수정)onEmpty
: 실행 허용Runnable
옵션이 비어 있는 경우peek
: (정의된 경우) 옵션의 값을 소비할 수 있도록 허용한다.- 그리고 그것은 또한
Serializable
Optional
즉, 메서드 인수 및 인스턴스 멤버로 안전하게 사용할 수 있다는 의미.
옵션은 자바의 선택적 "pseudo-monad"와 다른 모나드 법칙을 따르고 더 풍부한 API를 제공한다.물론 Java의 옵션(및 다른 방법): 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은 자바 타입이다.Optional javaOptional = opt.toJava()
, 매우 쉽다 :) 물론 변환은 다른 방식으로도 존재한다.Option option = Option.ofOptional(javaOptional)
.
N.B. Vavr은io.vavr.API
편리한 정적 방법이 많은 클래스 =)
추가 읽기
N.B. 이것은 Vavr이 제공하는 것(패턴 매칭, 스트림 a.k.a. 게으른 평가 목록, 모나치 유형, 불변의 수집 등)의 극히 작은 예일 뿐이다.
여기서의 문제:
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.
그때If empty.
, 그리고 그 표현은 null로 평가될 것이다.만일
!optional.isPresent()
, 시스템이 인쇄할 것이다.If empty.
, 그리고 그 표현은 null로 평가될 것이다.
기능이 제공된 경우map()
다른 값(다른 값)을 반환하면 코드는 예상대로 작동할 수 있으며, 코드는 다음과 같은 기능을 제공한다.map()
다음과 같은 경우에 실행된다.isPresent()
그리고 제공되는 기능orElseGet()
만일!isPresent()
:
예를 들어 다음과 같다.
optional
.map(data -> {
System.out.println("If present.");
return 0;
})
.orElseGet( () -> {
System.out.println("If empty.");
return 0;
});
실행 시:
만일
optional.isPresent()
, 시스템이 인쇄할 것이다.If present.
, 그리고 그 표현은 0으로 평가될 것이다.만일
!optional.isPresent()
, 시스템이 인쇄할 것이다.If empty.
, 그리고 그 표현은 0으로 평가될 것이다.
만약 당신의 구체적인 경우라면, 나는 당신이 당신의insert
그리고update
메서드는 다음과 유사한 코드를 사용할 수 있다.
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*/);
재료는 다음과 같다.
- "elseApply" 메서드 전용으로 기능 인터페이스가 거의 수정되지 않음
- 선택적 향상
- 약간의 커링 :-)
"비슷하게" 강화된 기능 인터페이스.
@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);
}
}
이것은 요령을 터득해야 하며 그러한 요건을 다루는 기본적인 템플릿이 될 수 있다.
여기서 기본적인 생각은 다음과 같다.비기능적 스타일 프로그래밍 세계에서는 첫 번째 매개 변수가 값을 사용할 수 있는 경우 실행해야 하는 실행 가능한 코드의 일종이고 다른 매개 변수는 값을 사용할 수 없는 경우 실행해야 하는 실행 가능한 코드인 경우 두 가지 매개 변수를 사용하는 방법을 구현할 수 있다.가독성을 높이기 위해 커링을 사용하여 두 파라미터의 함수를 각각 한 파라미터의 두 함수로 나눌 수 있다.이것이 내가 여기서 기본적으로 한 것이다.
힌트: Opt는 또한 값을 사용할 수 없는 경우 코드를 실행하려는 다른 사용 사례도 제공한다.이 작업은 옵션도 통해 수행될 수 있다.filter.stuff 그러나 나는 이것이 훨씬 읽기 쉽다는 것을 알았다.
그게 도움이 되길 바래!
추가 정보:
"만약 그렇지 않다면"이라고 말하는 또 다른 방법은 다음과 같다.
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()
존재하지 않는지 확인하는 것.
'programing' 카테고리의 다른 글
C 소스 파일을 다른 파일에 포함하시겠습니까? (0) | 2022.04.22 |
---|---|
우선순위 변경최대 우선 순위 큐로 대기열 지정 (0) | 2022.04.22 |
C에서 2D 어레이를 0으로 설정하는 가장 빠른 방법? (0) | 2022.04.22 |
C 또는 C++가 어레이 < 어레이 + SIZE>를 보증하는가? (0) | 2022.04.21 |
Nuxt vuex 상태 메뉴목록:구성 요소에서 정의되지 않음 (0) | 2022.04.21 |