Java 8의 Optional.ifPresent 및 if-not-present의 기능적 스타일?
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
하여 데이터베이스에 저장합니다.
두 개의 람다에 주목해야한다 null
.
그러나 opt
존재하면 두 람다가 모두 실행됩니다. obj
업데이트되고 새 개체가 데이터베이스에 저장됩니다. 이것은 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);
이 새로운 코드에는 3 가지가 있습니다.
- 기존 객체를 쉽게 사용하기 전에 기능을 정의 할 수 있습니다.
- 각 옵션에 대해 객체 굴절을 생성하지 않음 옵션은 하나 뿐이며 메모리가 적고 GC가 적습니다.
- 다른 구성 요소와 더 나은 사용을 위해 소비자를 구현하고 있습니다.
그건 그렇고 지금은 그 이름이 더 묘사 적입니다. 실제로 소비자입니다.>
Java 9 이상을 사용하는 경우 다음 ifPresentOrElse()
방법 을 사용할 수 있습니다 .
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
Java 8 치트 시트의 탁월한 Optional을 참조하십시오 .
대부분의 사용 사례에 대한 모든 답변을 제공합니다.
아래 요약
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 ()-비어 있음
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
orElseThrow ()-비어있는 경우 예외적으로 예외 처리
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"));
설명 된 동작은 대부분의 Scala 구문을 구현하는 Java 8+ 용 객체 기능 라이브러리 인 Vavr (이전의 Javaslang이라고 함) 을 사용하여 달성 할 수 있습니다 (Scala는 더 풍부한 유형 시스템을 JVM에 구축 한보다 표현적인 언어 임). 순수 기능 코드를 작성하기 위해 Java 프로젝트에 추가하는 것은 매우 좋은 라이브러리입니다.
Vavr은 다음 Option
과 같은 옵션 유형에서 작동하는 기능을 제공하는 모나드를 제공합니다.
fold
: 두 경우 모두 옵션 값을 맵핑합니다 (정의 / 빈).onEmpty
:Runnable
옵션이 비어있을 때 실행할 수 있습니다peek
: 옵션 값을 정의 할 수 있습니다 (정의 된 경우).- 또한
Serializable
반대로Optional
메소드 인수 및 인스턴스 멤버로 안전하게 사용할 수 있습니다.
Option은 Java의 Optional "pseudo-monad"와 다른 모나드 법칙을 따르며보다 풍부한 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)
);
추가 자료
NB 이것은 Vavr이 제공하는 것의 아주 작은 예일뿐입니다 (패턴 일치, 지연 평가 된 스트림, 모나 딕 타입, 불변 컬렉션 등).
다른 해결책은 다음과 같습니다.
이것은 당신이 그것을 사용하는 방법입니다 :
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"메소드를위한 약간의 수정 된 기능 인터페이스
- 선택적 향상
- 약간의 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를 통해 수행 할 수도 있지만 훨씬 더 읽기 쉽습니다.
희망이 도움이됩니다!
좋은 프로그래밍 :-)
값을 저장하려는 경우 :
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' 카테고리의 다른 글
MySQL 한 테이블과 다른 테이블에서 모든 열을 선택하십시오. (0) | 2020.04.12 |
---|---|
자바 스크립트에서 (키, 값)을 반복하는 방법은 무엇입니까? (0) | 2020.04.12 |
HTML5 비디오가 끝났을 때 감지 (0) | 2020.04.12 |
pytest에서 예외가 발생했다고 올바르게 주장하는 방법은 무엇입니까? (0) | 2020.04.12 |
Visual Studio에서 현재 프로젝트를 빌드하기위한 표준 키보드 단축키가 있습니까? (0) | 2020.04.12 |