Java 8 : Lambda-Streams, 예외가있는 메소드 별 필터링


168

Java 8의 Lambda 표현식을 시도하는 데 문제가 있습니다. 일반적으로 정상적으로 작동하지만 이제는 throw하는 메소드가 IOException있습니다. 다음 코드를 살펴 보는 것이 가장 좋습니다.

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

문제는 isActive- 및 getNumber-Methods의 가능한 예외를 잡아야하기 때문에 컴파일되지 않는다는 것입니다. 그러나 아래처럼 try-catch-Block을 명시 적으로 사용하더라도 예외를 포착하지 않기 때문에 여전히 컴파일되지 않습니다. 따라서 JDK에 버그가 있거나이 예외를 잡는 방법을 모르겠습니다.

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

어떻게 작동합니까? 누군가가 올바른 해결책을 알려줄 수 있습니까?




4
간단하고 정답 : 람다 내에서 예외를 잡아라.
Brian Goetz

답변:


211

람다를 탈출 하기 전에 예외 잡아야합니다 .

s = s.filter(a -> { try { return a.isActive(); } 
                    catch (IOException e) { throw new UncheckedIOException(e); }}});

람다는 작성하는 장소에서 평가되지 않고 JDK 클래스 내에서 완전히 관련이없는 장소에서 평가된다는 사실을 고려하십시오. 그래서 그것은 확인 된 예외가 발생하는 지점이 될 것이며, 그 장소에서 선언되지 않았습니다.

확인 된 예외를 확인되지 않은 예외로 변환하는 람다 래퍼를 사용하여 처리 할 수 ​​있습니다.

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (RuntimeException e) { throw e; }
  catch (Exception e) { throw new RuntimeException(e); }
}

귀하의 예는 다음과 같이 작성됩니다

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

내 프로젝트에서 나는 포장 없이이 문제를 처리합니다. 대신 컴파일러의 예외 검사를 효과적으로 제거하는 방법을 사용합니다. 말할 것도없이, 이것은주의해서 다루어야하며, 프로젝트의 모든 사람들은 체크되지 않은 예외가 선언되지 않은 곳에 나타날 수 있음을 알고 있어야합니다. 이것은 배관 코드입니다.

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
  try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }


@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

그리고 당신은 그것을 선언하지 IOException않더라도 당신의 얼굴에 던져 질 것을 기대할 수 있습니다 collect. 에서 전부는 아니지만 대부분의 실제 사례 당신은 어쨌든, 예외를 다시 발생하고, 일반 오류로 처리하는 것입니다. 이 모든 경우에 명확성 또는 정확성이 손실되지 않습니다. 그 자리에서 예외에 실제로 반응하기를 원하는 다른 경우에주의하십시오. 개발자는 IOException컴파일러가이를 포착 할 수 있다는 사실을 인식하지 못하며 실제로 예외를 던질 수 없다고 믿었 기 때문에이를 포착하려고하면 실제로 불만을 제기하게됩니다.


4
NettyIO가 이전에 "밀폐 투사"를하는 것을 보았고 의자를 창문 밖으로 내 버리고 싶었습니다. "무엇입니까? 그 예외를 확인한 곳은 어디입니까?" 이것은 내가 아직 본 몰래 던지는 최초의 합법적 사용 사례입니다. 프로그래머는 몰래 던질 수 있다는 표시에주의를 기울여야합니다. 어쩌면 확인 된 예외를 지원하는 다른 스트림 인터페이스 / impl을 만드는 것이 더 낫습니까?
kevinarpe

8
정상적인 API는 선언되지 않은 확인 된 예외를 클라이언트에 전달해서는 안됩니다. 그러나 API 에서 확인 된 예외가 누출 될 수 있음을 이해할 수 있습니다. 그들은 일반적인 실패의 또 다른 징후 일뿐 아니라 도매로 잡히고 처리 되기만하면 해를 끼치 지 않습니다.
Marko Topolnik

5
@kevinarpe 이것은 비열한 던지기가 나쁜 생각 인 정확한 이유입니다. 컴파일러를 단락시키는 것은 미래의 유지 보수 자들을 혼란스럽게 만듭니다.
Thorbjørn Ravn Andersen

29
규칙이 마음에 들지 않는다고해서 법을 자신의 손에 맡기는 것은 좋은 생각이 아닙니다. 프로그램의 투명성 및 유지 관리성에 대한 훨씬 중요한 고려 사항보다 코드 작성기의 편의성을 제공하므로 조언은 무책임합니다.
Brian Goetz

34
@brian 단지 무언가가 규칙이라고해서 그것이 좋은 생각임을 의미하지는 않습니다. 그러나 내가 해결책으로 제안한 내용과 관심있는 독자에게 FYI로 제공하는 내용을 풍부한 고지 사항으로 특허 적으로 밝힌 것으로 생각했기 때문에 내 답변의 두 번째 부분을 "조언"이라고 언급 한 것에 놀랐습니다.
Marko Topolnik

29

람다로 정적 통증을 전파 할 수 있으므로 모든 것이 읽기 쉽습니다.

s.filter(a -> propagate(a::isActive))

propagate여기서 java.util.concurrent.Callable매개 변수로 수신 하고 호출 중에 발생한 예외를로 변환합니다 RuntimeException. 비슷한 변환 방법이 있습니다. Throwables # propagate (Throwable)구아바에도 가 있습니다.

이 방법은 람다 방법 체인에 필수적인 것으로 보이므로 언젠가 인기있는 라이브러리 중 하나에 추가 되거나이 전파 동작이 기본적으로 이루어지기를 바랍니다.

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}

22

UtilException헬퍼 클래스를 사용하면 다음과 같이 Java 스트림에서 확인 된 예외를 사용할 수 있습니다.

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

참고 Class::forName발생 ClassNotFoundException하는, 확인 . 스트림 자체도 ClassNotFoundException검사하고 검사되지 않은 예외는 줄 바꿈하지 않습니다.

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
    R apply(T t) throws E;
    }

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
    return t -> {
        try { return function.apply(t); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }

}

사용 방법에 대한 다른 많은 예제 (정적으로 가져온 후 UtilException) :

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

그러나 다음과 같은 장점, 단점 및 제한 사항을 이해하기 전에 사용하지 마십시오 .

• 호출 코드가 확인 된 예외를 처리하는 경우 스트림을 포함하는 메소드의 throws 절에 예외를 추가해야합니다. 컴파일러는 더 이상 강제로 추가하지 않으므로 잊어 버리기가 더 쉽습니다.

• 호출 코드가 이미 확인 된 예외를 처리하는 경우 컴파일러는 스트림을 포함하는 메소드 선언에 throws 절을 추가하도록 알려줍니다 (그렇지 않은 경우 : 해당 try 문의 본문에 예외가 발생하지 않습니다). ).

• 어떤 경우에도 스트림을 포함하는 메서드 내부에서 확인 된 예외를 포착하기 위해 스트림 자체를 둘러 쌀 수 없습니다 (시도하면 컴파일러는 다음과 같이 말합니다. 해당 try 문의 본문에는 예외가 발생하지 않습니다).

• 선언 된 예외를 문자 그대로 던질 수없는 메서드를 호출하는 경우 throws 절을 포함하지 않아야합니다. 예를 들어, new String (byteArr, "UTF-8")은 UnsupportedEncodingException을 발생 시키지만 Java 스펙에서는 UTF-8이 항상 존재하도록 보장합니다. 던지기 선언은 성가신 일이며 최소한의 상용구로 침묵시키는 해결책은 환영합니다.

• 확인 된 예외를 증오하고 처음으로 Java 언어에 추가해서는 안된다고 생각하는 경우 (많은 사람들이 이런 식으로 생각하며 그 중 하나가 아닙니다), 확인 된 예외를 추가하지 마십시오. 스트림을 포함하는 메소드의 절을 던집니다. 그런 다음 확인 된 예외는 확인되지 않은 예외처럼 작동합니다.

• throws 선언을 추가 할 수있는 옵션이없는 엄격한 인터페이스를 구현하고 있지만 예외를 throw하는 것이 전적으로 적합한 경우 예외를 래핑하는 권한을 얻기 위해 예외를 래핑하면 가짜 예외가있는 스택 추적이 발생합니다. 실제로 무엇이 잘못되었는지에 대한 정보를 제공하지 않습니다. 확인 된 예외를 발생시키지 않는 Runnable.run ()이 좋은 예입니다. 이 경우, 점검 된 예외를 스트림을 포함하는 메소드의 throws 절에 추가하지 않기로 결정할 수 있습니다.

• 어떤 경우에도 스트림을 포함하는 메소드의 throws 절에 확인 된 예외를 추가하지 않거나 추가하는 것을 잊은 경우 CHECKED 예외가 발생하는 다음 두 가지 결과에 유의하십시오.

1) 호출 코드는 이름으로 붙잡을 수 없습니다 (시도하면 컴파일러는 다음과 같이 말합니다 : 해당 try 문 본문에는 예외가 발생하지 않습니다). 그것은 아마도 "캐치 예외"또는 "캐치 던지기 가능"에 의해 메인 프로그램 루프에서 잡히고 어쩌면 원하는 것일 수도 있습니다.

2) 최소한의 놀라움의 원칙을 위반합니다. 더 이상 RuntimeException을 포착하여 가능한 모든 예외를 포착 할 수있을만큼 충분하지 않습니다. 따라서 프레임 워크 코드에서는 수행하지 말고 완전히 제어하는 ​​비즈니스 코드에서만 수행해야한다고 생각합니다.

결론 : 나는 여기의 한계가 심각하지 않다고 생각하며 UtilException클래스는 두려움없이 사용될 수 있습니다. 그러나 그것은 당신에게 달려 있습니다!


8

Stream람다를 감싸서 검사되지 않은 예외를 throw 한 다음 나중에 터미널 작업에서 검사되지 않은 예외를 풀어서 변형을 롤링 할 수 있습니다 .

@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
    public boolean test(T t) throws X;
}

@FunctionalInterface
public interface ThrowingFunction<T, R, X extends Throwable> {
    public R apply(T t) throws X;
}

@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}

public interface ThrowingStream<T, X extends Throwable> {
    public ThrowingStream<T, X> filter(
            ThrowingPredicate<? super T, ? extends X> predicate);

    public <R> ThrowingStream<T, R> map(
            ThrowingFunction<? super T, ? extends R, ? extends X> mapper);

    public <A, R> R collect(Collector<? super T, A, R> collector) throws X;

    // etc
}

class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamAdapter(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new AdapterException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamAdapter<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamAdapter<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (AdapterException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskException(() -> delegate.collect(collector));
    }
}

그런 다음이와 동일한 방식으로 사용할 수 있습니다 Stream.

Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());

이 솔루션에는 약간의 상용구가 필요하기 때문에 이미 작성한 라이브러리를 살펴보고 전체 Stream클래스 (및 더 많은 것)에 대해 여기에서 설명한 것을 정확하게 수행하는 것이 좋습니다 .


안녕하세요 ... 작은 버그? new StreamBridge<>(ts, IOException.class);->new StreamBridge<>(s, IOException.class);
kevinarpe

1
@kevinarpe p. 또한 말했다 StreamAdapter.
Jeffrey

5

#propagate () 메소드를 사용하십시오. Sam Beran의 Java 8 블로그 에서 비 구아바 구현 샘플 :

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}

Java 8 블로그 링크가 작동하지 않습니다.
Spycho

4

이것은 질문에 직접 대답하지 않지만 (많은 다른 답변이 있습니다) 먼저 문제를 피하려고 시도합니다.

내 경험상 예외 Stream(또는 다른 람다 식) 에서 예외를 처리해야 할 필요성 은 예외가 발생해서는 안되는 메소드에서 발생하도록 선언되었다는 사실에서 비롯됩니다. 이것은 종종 비즈니스 로직과 입출력을 혼합하여 발생합니다. 귀하의 Account인터페이스는 완벽한 예입니다 :

interface Account {
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
}

IOException각 게터 를 던지는 대신 다음 디자인을 고려하십시오.

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

이 메소드 AccountReader.readAccount(…)는 데이터베이스 나 파일 등에서 계정을 읽고 성공하지 못하면 예외를 던질 수 있습니다. Account사용할 준비가 된 모든 값이 이미 포함 된 객체를 구성합니다 . 값이 이미로드되었으므로readAccount(…) 이므로 getter는 예외를 발생시키지 않습니다. 따라서 예외를 감싸거나 숨기거나 숨길 필요없이 람다에서 자유롭게 사용할 수 있습니다.

물론 내가 설명한 방식으로 항상 할 수있는 것은 아니지만 종종 가능하며 더 깨끗한 코드 (IMHO)로 이어집니다.

  • 더 나은 우려 분리단일 책임 원칙 준수
  • 보일러 플레이트 감소 : 코드를 복잡하게 만들 필요가 없습니다. throws IOException 컴파일러를 만족시키기 위해
  • 오류 처리 : 필드 값을 얻으려는 경우에만 비즈니스 논리 중간 위치 대신 파일 또는 데이터베이스에서 읽을 때 오류가 발생하는 위치에서 오류를 처리합니다.
  • 불변 으로 만들 수 있습니다Account 그것의 장점 (예를 들어 스레드 안전)에서 이익
  • Account람다 (예 :에서 Stream) 에 사용하기 위해 "더러운 트릭"이나 해결 방법이 필요하지 않습니다.

4

AbacusUtil 에서 StreamTry 를 사용 하여 간단한 코드 아래에서 해결할 수 있습니다 .

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

공개 : 저는 개발자입니다 AbacusUtil.


3

@marcg 솔루션을 확장하면 일반적으로 Streams에서 확인 된 예외를 throw하고 잡을 수 있습니다 . 즉, 컴파일러 는 외부 스트림처럼 캐치 / 다시 던지도록 요청할 것입니다 !!

@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
    boolean test(T t) throws E;
}

/**
 * .filter(rethrowPredicate(t -> t.isActive()))
 */
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
    return t -> {
        try {
            return predicate.test(t);
        } catch (Exception exception) {
            return throwActualException(exception);
        }
    };
}

@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwActualException(Exception exception) throws E {
    throw (E) exception;
}

그런 다음 예제는 다음과 같이 작성됩니다 (보다 명확하게 표시하기 위해 테스트 추가).

@Test
public void testPredicate() throws MyTestException {
    List<String> nonEmptyStrings = Stream.of("ciao", "")
            .filter(rethrowPredicate(s -> notEmpty(s)))
            .collect(toList());
    assertEquals(1, nonEmptyStrings.size());
    assertEquals("ciao", nonEmptyStrings.get(0));
}

private class MyTestException extends Exception { }

private boolean notEmpty(String value) throws MyTestException {
    if(value==null) {
        throw new MyTestException();
    }
    return !value.isEmpty();
}

@Test
public void testPredicateRaisingException() throws MyTestException {
    try {
        Stream.of("ciao", null)
                .filter(rethrowPredicate(s -> notEmpty(s)))
                .collect(toList());
        fail();
    } catch (MyTestException e) {
        //OK
    }
}

이 예제는 컴파일되지 않습니다
Roman M

안녕하세요. @RomanM이 점을 지적 해 주셔서 감사합니다. "throwActualException"메소드에서 누락 된 리턴 유형을 수정했습니다. 우리는 이것을 프로덕션 환경에서 사용하고 있으므로 귀하의 측면에서도 효과가 있기를 바랍니다.
PaoloC

3

IOException (RuntimeException에) 처리 코드를 올바르게 추가하려면 메소드는 다음과 같습니다.

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

이제 문제는 IOException로 캡처되어 RuntimeException다시로 변환되어야한다는 것입니다IOException 되어야하며, 위의 방법에 더 많은 코드를 추가해야한다는 것입니다.

Stream이와 같이 수행 할 수있을 때 왜 사용해야합니까? 메서드가 발생 IOException하므로 추가 코드가 필요하지 않습니다.

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;

1

이 문제를 염두에두고 확인 된 예외 및 람다를 처리하기위한 작은 라이브러리를 개발했습니다. 사용자 정의 어댑터를 사용하면 기존 기능 유형과 통합 할 수 있습니다.

stream().map(unchecked(URI::new)) //with a static import

https://github.com/TouK/ThrowingFunction/


1

귀하의 예는 다음과 같이 쓸 수 있습니다 :

import utils.stream.Unthrow;

class Bank{
   ....
   public Set<String> getActiveAccountNumbers() {
       return accounts.values().stream()
           .filter(a -> Unthrow.wrap(() -> a.isActive()))
           .map(a -> Unthrow.wrap(() -> a.getNumber()))
           .collect(Collectors.toSet());
   }
   ....
}

Unthrow 클래스는 여기 취할 수 https://github.com/SeregaLBN/StreamUnthrower


0

타사 라이브러리를 사용하는 것이 마음에 들지 않으면 AOL의 cyclops-react lib, reveal :: contributor이며 여기에 도움 이되는 ExceptionSoftener 클래스가 있습니다.

 s.filter(softenPredicate(a->a.isActive()));

0

Java의 기능적 인터페이스는 점검되거나 점검되지 않은 예외를 선언하지 않습니다. 메소드 서명을 다음과 같이 변경해야합니다.

boolean isActive() throws IOException; 
String getNumber() throwsIOException;

에:

boolean isActive();
String getNumber();

또는 try-catch 블록으로 처리하십시오.

public Set<String> getActiveAccountNumbers() {
  Stream<Account> s =  accounts.values().stream();
  s = s.filter(a -> 
    try{
      a.isActive();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  Stream<String> ss = s.map(a -> 
    try{
      a.getNumber();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  return ss.collect(Collectors.toSet());
}

다른 옵션은 사용자 정의 랩퍼를 작성하거나 ThrowingFunction과 같은 라이브러리를 사용하는 것입니다. 라이브러리를 사용하면 pom.xml에 종속성 만 추가하면됩니다.

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

그리고 ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier와 같은 특정 클래스를 사용하십시오.

결국 코드는 다음과 같습니다.

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}

0

스트림 (Java -8)에서 확인 된 예외를 처리하는 방법을 찾지 못했습니다. 내가 적용 한 유일한 방법은 스트림에서 확인 된 예외를 catch하고 확인되지 않은 예외로 다시 던지는 것입니다.

        Arrays.stream(VERSIONS)
        .map(version -> TemplateStore.class
                .getClassLoader().getResourceAsStream(String.format(TEMPLATE_FILE_MASK, version)))
        .map(inputStream -> {
            try {
                return ((EdiTemplates) JAXBContext.newInstance(EdiTemplates.class).createUnmarshaller()
                        .unmarshal(inputStream)).getMessageTemplate();
            } catch (JAXBException e) {
                throw new IllegalArgumentException(ERROR, e);
            }})
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

실제로 질문에 대답하려고합니까?
Nilambar Sharma

@Nilambar-여기서 말하려고하는 것은 Java 스트림을 사용하는 동안 확인 된 예외를 처리 할 수있는 방법이 없다는 것입니다 ... 체크 된 것을 잡아서 런타임 / 체크되지 않은 것을 던지는 데 필요한 모든 것이 끝났습니다 .. 지금 두 가지가 있습니다. 1. 내 이해가 정확하지 않다고 생각되면 저를 정정하십시오. 또는 2. 생각하면, 내 게시물이 관련이 없습니다. 안부
사찬

0

스트림 내에서 예외를 처리하고 추가 예외를 계속 처리하려면 DZone의 Brian Vermeer by Either 개념을 사용 하는 훌륭한 기사가 있습니다. 이 상황을 처리하는 훌륭한 방법을 보여줍니다. 유일하게 누락 된 것은 샘플 코드입니다. 이것은 해당 기사의 개념을 사용한 탐구 샘플입니다.

@Test
public void whenValuePrinted_thenPrintValue() {

    List<Integer> intStream = Arrays.asList(0, 1, 2, 3, 4, 5, 6);
    intStream.stream().map(Either.liftWithValue(item -> doSomething(item)))
             .map(item -> item.isLeft() ? item.getLeft() : item.getRight())
             .flatMap(o -> {
                 System.out.println(o);
                 return o.isPresent() ? Stream.of(o.get()) : Stream.empty();
             })
             .forEach(System.out::println);
}

private Object doSomething(Integer item) throws Exception {

    if (item == 0) {
        throw new Exception("Zero ain't a number!");
    } else if (item == 4) {
        return Optional.empty();
    }

    return item;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.