Programing

Java 8의 Optional.ifPresent 및 if-not-present의 기능적 스타일?

lottogame 2020. 4. 12. 10:11
반응형

Java 8의 Optional.ifPresent 및 if-not-present의 기능적 스타일?


Java 8에서는 Optional객체가 있으면 객체에 무언가를 하고 싶지 않으면 다른 일을 하고 싶습니다 .

if (opt.isPresent()) {
  System.out.println("found");
} else {
  System.out.println("Not found");
}

그러나 이것은 '기능적 스타일'이 아닙니다.

OptionalifPresent()방법을하지만, 나는 체인 드릴 수 없습니다 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 가지가 있습니다.

  1. 기존 객체를 쉽게 사용하기 전에 기능을 정의 할 수 있습니다.
  2. 각 옵션에 대해 객체 굴절을 생성하지 않음 옵션은 하나 뿐이며 메모리가 적고 GC가 적습니다.
  3. 다른 구성 요소와 더 나은 사용을 위해 소비자를 구현하고 있습니다.

그건 그렇고 지금은 그 이름이 더 묘사 적입니다. 실제로 소비자입니다.>


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)
);

추가 자료

Null 참조, 억 달러의 실수

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*/);

이것은 재료입니다 :

  1. "elseApply"메소드를위한 약간의 수정 된 기능 인터페이스
  2. 선택적 향상
  3. 약간의 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 ()를 사용하여 존재하지 않는지 확인할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/23773024/functional-style-of-java-8s-optional-ifpresent-and-if-not-present

반응형