예외를 발생시키는 Java 8 Lambda 함수?


469

String매개 변수가 있고를 반환하는 메서드에 대한 참조를 만드는 방법을 알고 있습니다 int.

Function<String, Integer>

그러나 함수가 예외를 던지면 작동하지 않습니다.

Integer myMethod(String s) throws IOException

이 참조를 어떻게 정의합니까?



1
... 그리고 이것 하나 : stackoverflow.com/questions/31270759/…
Vadzim


4
모든 솔루션은 런타임 예외를 던지는 방법과 비슷합니다. 좋은 솔루션이 아니라고 믿습니다. 루프에 대해 오래된 Java를 사용하는 것이 좋습니다
Nazeel

5
무엇에 대해 jool의 도서관은? cf org.jooq.lambda. 체크되지 않은 패키지
chaiyachaiya

답변:


402

다음 중 하나를 수행해야합니다.

  • 코드 인 경우 검사 된 예외를 선언하는 고유 한 기능 인터페이스를 정의하십시오.

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }

    그것을 사용하십시오 :

    void foo (CheckedFunction f) { ... }
  • 그렇지 않으면 Integer myMethod(String s)확인 된 예외를 선언하지 않는 메소드를 래핑 하십시오.

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    그리고:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    또는:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };

7
실제로 확장 Consumer하거나 Function기본 방법을 사용할 수 있습니다 -아래 답변을 참조하십시오.
jlb December

2
나는 이것이 하나의 라이너 로 달성 될 수 있다고 생각합니다 .
Ned Twigg

6
사소한 최적화 : 대신에 (String t) -> myWrappedMethod(t)메소드 참조를 this::myWrappedMethod사용할 수도 있습니다.
Clashsoft

8
보다 일반적인 방법은 다음과 같이 확인 된 함수를 정의하는 것입니다. @FunctionalInterface 공용 인터페이스 CheckedFunction <T, R, E extends Exception> {R apply (T t) throw E; } 그런 식으로 함수에서 발생하는 예외를 정의하고 코드에 대해 인터페이스를 재사용 할 수도 있습니다.
Martin Odhelius

3
와. 자바가 생각보다 나빠
user275801

194

당신은 실제로 확장 할 수 있습니다 Consumer(그리고 Function자바 8의 사용 - 핸들 예외가있는 새로운 인터페이스 등)을 기본 방법을 !

이 인터페이스를 고려하십시오 (extends Consumer).

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

예를 들어 목록이있는 경우 :

final List<String> list = Arrays.asList("A", "B", "C");

forEach예외를 발생시키는 일부 코드로 (예 : with )을 사용하려면 일반적으로 try / catch 블록을 설정했을 것입니다.

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

그러나이 새로운 인터페이스를 사용하면 람다 식으로 인스턴스화 할 수 있으며 컴파일러는 불평하지 않습니다.

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

또는 더 간결하게 캐스팅하십시오! :

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

업데이트 : 외모의 아주 좋은 유틸리티 라이브러리 부분이처럼 두리안 라는 오류 많이 더 유연성이 문제를 해결하는 데 사용할 수 있습니다. 예를 들어, 위의 구현에서 오류 처리 정책 ( System.out...또는 throw RuntimeException)을 명시 적으로 정의 했지만 Durian의 오류를 사용하면 대규모 유틸리티 메소드를 통해 정책을 즉시 적용 할 수 있습니다. 공유해 주셔서 감사 합니다 , @NedTwigg !.

샘플 사용법 :

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));

14
따라서 인터페이스 세트 (함수, 소비자, 공급 업체 등)와 오류 처리 정책 (Throwing, System.out.println 등)이 있습니다. 붙여 넣기 "ThrowingConsumer, ThrowingFunction 등"을 복사하지 않고도 모든 종류의 기능으로 모든 정책을 쉽게 사용할 수 있는 방법이 있다고 생각 합니다.
Ned Twigg

1
언젠가는 ... 확인되지 않은 예외를 사용하기로 결정하고 추가 기능 인터페이스 또는 새로운 라이브러리를 사용하지 않기로 결정했습니다.
aliopi

1
다음은 몰래 던지는 관용구를 사용 하는 개선 된 버전 입니다. RuntimeException을 CheckException으로 랩핑 해제 할 필요가 없습니다.
myui

61

두리안의 Errors수업 은 위의 다양한 제안의 많은 장점을 결합한 것 같습니다 .

프로젝트에 Durian을 포함 시키려면 다음 중 하나를 수행하십시오.


또는 스트림에 고유 한 오류 처리가 필요하기 때문에 RxJava를 사용할 수 있으며 파이프 라인에 예외를 발생시키는 무언가가있을 경우 아마도 관찰 가능한 스트림 일 가능성이 높습니다. 이것은 또한 라이브러리의 다운 스트림 소비자에게 Java 8을 강요하지 않습니다.
Adam Gent

2
있습니다 두리안 6월 2016 아니 쇼 스토퍼 이후 새로운 버전이 있습니다,하지만 뭔가 마음에 계속.
Istvan Devai

9
두리안 관리자입니다. 뭐가 고장났어? 사용자가 버그 또는 중요한 누락 된 기능을 발견하면 버그 수정을 신속하게 릴리스합니다. 라이브러리는 간단하므로 버그 보고서가 없으므로 버그 수정을 릴리스 할 필요가 없습니다.
Ned Twigg

28

이것은 Java 8에만 국한된 것이 아닙니다. 다음과 동등한 것을 컴파일하려고합니다.

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}

15
문제는 "이 참조를 어떻게 정의합니까?"입니다. . 이것은 실제로 질문에 대답하지 않습니다. 문제가 무엇인지 명확하게 설명합니다.
다우드 이븐 카림

13

면책 조항 : 아직 Java 8을 사용하지 않았으며 그것에 대해 읽었습니다.

Function<String, Integer>던지지 IOException않으므로 코드를 넣을 수 없습니다 throws IOException. 를 기대하는 메소드를 호출하는 경우 Function<String, Integer>해당 메소드에 전달하는 람다는 IOException마침표를 던질 수 없습니다 . 다음과 같이 람다를 작성할 수 있습니다 (확실하지 않은 람다 구문이라고 생각합니다).

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

또는 람다를 전달하는 메소드가 직접 작성한 메소드 인 경우 새 기능 인터페이스를 정의하고 대신 매개 변수 유형으로 사용할 수 있습니다 Function<String, Integer>.

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}

인터페이스 앞에 @FunctionalInterface 주석을 추가하면 람다에 대해서만 사용할 수 있습니다.
Gangnus

13
@ Gangnus : @FunctionalInterface람다에 주석을 사용할 수 있도록 주석이 필요하지 않습니다. 그래도 위생 검사에 권장됩니다.
Tanmay Patil 2016

9

타사 라이브러리를 사용하지 않아도되는 경우 ( Vavr ) 다음을 작성할 수 있습니다.

CheckedFunction1<String, Integer> f = this::myMethod;

또한 오류를 처리하는 소위 Try 모나드가 있습니다.

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

더 읽어주세요 여기를 하십시오 .

면책 조항 : 저는 Vavr의 제작자입니다.


7

던지지 않은 래퍼를 사용할 수 있습니다

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

또는

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);

6

그러나 아래와 같이 자신 만의 FunctionalInterface를 작성할 수 있습니다.

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

그런 다음 아래 표시된 것처럼 Lambdas 또는 참조를 사용하여 구현하십시오.

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}

6

할 수 있습니다.

@marcg를 확장 UtilException하고 <E extends Exception>필요한 곳에 generic을 추가 하십시오.이 방법으로 컴파일러는 throw 절과 모든 것을 Java 8 스트림에서 기본적으로 확인 된 예외를 던질 수있는 것처럼 다시 추가하도록합니다.

public final class LambdaExceptionUtil {

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

    /**
     * .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) {
                throwActualException(exception);
                return null;
            }
        };
    }

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

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
        assertEquals(2, sizes.size());
        assertEquals(4, sizes.get(0).intValue());
        assertEquals(5, sizes.get(1).intValue());
    }

    private Integer transform(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
        return value.length();
    }

    private static class MyTestException extends Exception { }
}

5

람다 내부에서 Class.forName 및 Class.newInstance 에이 문제가 있었으므로 방금 수행했습니다.

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

람다 내부에서 Class.forName ( "myClass"). newInstance ()를 호출하는 대신 uncheckedNewInstanceForName ( "myClass")을 호출했습니다.


4

함수 래퍼를 사용하는 또 다른 솔루션은 결과의 래퍼 인스턴스를 반환하는 것입니다. 모든 것이 제대로 진행되면 성공이라고 말하고 실패라고합니다.

일을 명확히하는 코드 :

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

간단한 사용 사례 :

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());

4

이 문제는 저를 귀찮게했습니다. 이것이 제가이 프로젝트를 만든 이유입니다 입니다.

그것으로 당신은 할 수 있습니다 :

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

JDK에 의해 정의 된 39 개의 인터페이스가 있습니다 Throwing. 이것들은 모두 @FunctionalInterface스트림에서 사용됩니다 (기본 Stream뿐만 아니라 IntStream, LongStreamDoubleStream).

그리고 각각이 던지지 않는 상대를 확장함에 따라 람다에서도 직접 사용할 수 있습니다.

myStringStream.map(f) // <-- works

기본 동작은 던지는 람다가 확인 된 예외를 던지면 ThrownByLambdaException확인 된 예외가 원인으로 발생합니다. 따라서이를 캡처하여 원인을 얻을 수 있습니다.

다른 기능도 사용할 수 있습니다.


정말 생각처럼, 나는 단지 여기에 제안 당신이 throw 가능 객체가 일반적인 만든 소원 : javaspecialists.eu/archive/Issue221.html를 , 예를 들면 : @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }-이 사용자가 캐치 할 필요가 없다 방법 Throwable,하지만 특정 대신 예외를 확인했습니다.
Zoltán

@ Zoltán 그것은 예외를 선언 할 때마다 고통 스러울 것입니다. 또한 항상 .doApply () 대신 .apply ()를 사용하고 catch ThrownByLambdaException할 수 있습니다. 원래 예외가 원인이 될 수 있습니다 (또는 사용할 수 있습니다 rethrow(...).as(MyRuntimeException.class))
fge

나는 이것 주위에 (종류의) 방법이 있다고 생각 합니다 .
Ned Twigg

@NedTwigg 나는 오래 전에 이것을 해결했다. 이제 Throwing.runnable()체인 기능을 사용 하여 다른 사용자도 사용할 수 있습니다.
fge

체인 기능이 매우 멋집니다! 내 의견은 ThrowingRunnable에 일반적인 예외가 있는지 여부에 관한 것입니다. Zoltan은 라이브러리가 일반 매개 변수로 인수를 가질 수 있는지 물었고 사용하기가 어려울 것이라고 말했습니다. 내 링크는 코드 라인에 대한 링크로 예외없이 고통을 피할 수있는 일반적인 방법을 보여줍니다. 내가 잘못 읽지 않으면 라이브러리의 예외는 일반적인 것이 아닙니다 (일반적인 디자인으로 유용하지는 않기 때문에 합리적인 디자인 선택입니다).
네드 트위그

4

여기에 이미 게시 된 훌륭한 답변이 많이 있습니다. 다른 관점에서 문제를 해결하려고합니다. 그것은 단지 2 센트입니다. 어딘가에 틀렸다면 수정하십시오.

FunctionalInterface의 Throws 절은 좋은 생각이 아닙니다

다음과 같은 이유로 IOException 발생을 강제하는 것이 좋지 않을 것이라고 생각합니다.

  • 이것은 Stream / Lambda의 안티 패턴처럼 보입니다. 전체 아이디어는 호출자가 제공 할 코드와 예외 처리 방법을 결정한다는 것입니다. 많은 시나리오에서 IOException이 클라이언트에 적용되지 않을 수 있습니다. 예를 들어, 클라이언트가 실제 I / O를 수행하는 대신 캐시 / 메모리에서 가치를 얻는 경우.

  • 또한 스트림에서의 예외 처리는 실제로 무시 무시합니다. 예를 들어 API를 사용하면 코드가 다음과 같이 표시됩니다.

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });

    못 생겼어? 또한 첫 번째 요점에서 언급했듯이 doSomeOperation 메소드는 (클라이언트 / 호출자의 구현에 따라) IOException을 발생시킬 수도 있고 발생하지 않을 수도 있지만, FunctionalInterface 메소드의 throws 절로 인해 항상 시험해보십시오.

이 API가 IOException을 발생시키는 것을 알고 있다면 어떻게해야합니까

  • 그렇다면 아마도 우리는 FunctionalInterface와 일반적인 인터페이스를 혼동하고있을 것입니다. 이 API가 IOException을 발생 시킨다는 것을 알고 있다면 대부분의 기본 / 추상 동작도 알고있을 것입니다. 다음과 같이 인터페이스를 정의하고 라이브러리를 기본 / 추상 구현으로 배포해야한다고 생각합니다.

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }

    그러나 try-catch 문제는 여전히 클라이언트에 존재합니다. 스트림에서 API를 사용하는 경우 여전히 끔찍한 try-catch 블록에서 IOException을 처리해야합니다.

  • 다음과 같이 기본 스트림 친화적 인 API를 제공하십시오.

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }

    기본 메소드는 소비자 오브젝트를 인수로 사용하여 예외를 처리합니다. 이제 클라이언트의 관점에서 코드는 다음과 같습니다.

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());

    좋은가요? 물론 Exception :: printStackTrace 대신 로거 또는 기타 처리 로직을 사용할 수 있습니다.

  • https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- 과 유사한 메소드를 노출 할 수도 있습니다 . 이전 메소드 호출의 예외를 포함 할 다른 메소드를 노출 할 수 있음을 의미합니다. 단점은 이제 API를 Stateful 상태로 만드는 것이므로 스레드 안전성을 처리해야하고 결국 성능에 영향을 줄 수 있습니다. 그래도 고려해야 할 옵션입니다.


확인 된 예외를 확인되지 않은 예외로 변환하거나 예외를 삼키는 것은 예외가 Stream발생한 요소를 알 수있는 방법이 없기 때문에 좋은 생각이 아닙니다 . 따라서 예외 처리기를 사용하고 유효하지 않은 결과를 필터링한다는 아이디어가 마음에 듭니다. MyAmazingAPI는 사실상 FunctionalInterface(따라서 @FunctionalInterface 주석을 추가 할 수 있음) 유의하십시오 . 또한을 사용하는 대신 기본값을 사용할 수 Optional.empty()있습니다.
Julien Kronegg '

4

몰래 던지는 관용구CheckedExceptionLambda 표현을 우회 할 수 있습니다 . a CheckedException를 감싸는 것은 RuntimeException엄격한 오류 처리에 좋지 않습니다.

ConsumerJava 콜렉션에서 사용되는 함수로 사용할 수 있습니다 .

다음은 지브의 대답에 대한 간단하고 개선 된 버전입니다 .

import static Throwing.rethrow;

@Test
public void testRethrow() {
    thrown.expect(IOException.class);
    thrown.expectMessage("i=3");

    Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
        int i = e.intValue();
        if (i == 3) {
            throw new IOException("i=" + i);
        }
    }));
}

이 단지에서 람다 wrapps 다시 throw를 . 그것은 수 CheckedException있는 다시 발생 Exception하여 람다에 던져졌다 것을.

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
        return consumer;
    }

    /**
     * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
     * allows the unchecked exception to propagate.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

여기 에서 전체 코드 및 단위 테스트를 찾으 십시오 .


3

이를 위해 ET 를 사용할 수 있습니다 . ET는 예외 변환 / 번역을위한 작은 Java 8 라이브러리입니다.

ET를 사용하면 다음과 같습니다.

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslator인스턴스는 스레드로부터 안전하며 여러 구성 요소에서 공유 할 수 있습니다. 원하는 경우보다 구체적인 예외 변환 규칙 (예 :)을 구성 할 수 있습니다 FooCheckedException -> BarRuntimeException. 사용할 수있는 다른 규칙이 없으면 확인 된 예외는로 자동 변환됩니다 RuntimeException.

(면책 조항 : 저는 ET의 저자입니다)


2
이 라이브러리의 저자 인 것 같습니다. 에 따르면 SO 규칙 , 당신은 있어야합니다 귀하의 답변에 제휴를 개시하고있다. 이 라이브러리를 작성한 답변에 명시 적으로 추가하십시오 (기타 ET 관련 답변과 동일).
Tagir Valeev 2

2
안녕하세요 Tagir, 힌트 주셔서 감사합니다. 나는 대답을 업데이트했다.
micha

2

내가하고있는 일은 사용자가 예외의 경우 실제로 원하는 값을 줄 수 있도록하는 것입니다. 그래서 이런 식으로 뭔가를했습니다

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

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

그리고 이것은 다음과 같이 호출 될 수 있습니다 :

defaultIfThrows(child -> child.getID(), null)

1
이것은 "초기 값"전략 (답변 에서처럼)과 기본값이 필요하지 않은 "rethrow RuntimeException"전략을 구별하는이 아이디어의 확장입니다.
Ned Twigg

2

cyclops-react 와 함께 타사 라이브러리를 사용하는 것이 마음에 들지 않으면 FluentFunctions API를 사용하여 작성할 수 있습니다

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked는 jOOλ CheckedFunction을 가져 와서 참조를 연화되지 않은 표준 JDK java.util.function.Function으로 되돌립니다.

또는 FluentFunctions API를 통해 캡처 된 기능을 계속 사용할 수 있습니다!

예를 들어, 메소드를 실행하고 최대 5 회 재 시도하고 기록 할 수있는 상태를 기록합니다.

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");

2

기본적으로 Java 8 함수 는 예외를 던질 수 없으며 여러 답변에서 제안한 것처럼 여러 가지 방법으로 달성 할 수 있습니다. 한 가지 방법은 다음과 같습니다.

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

다음과 같이 정의하십시오.

private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
    if ("abc".equals(str)) {
        throw new IOException();
    }
  return 1;
};

그리고 호출자 메소드에서 throws또는 try/catch동일한 예외를 추가하십시오 .


2

확인 된 예외를 전파 할 사용자 정의 리턴 유형을 작성하십시오. 이는 기능 인터페이스의 메소드에서 "throws exception"을 약간 수정하여 기존 기능 인터페이스를 미러링하는 새 인터페이스를 작성하는 대안입니다.

정의

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

용법

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

무슨 일이야?

확인 된 예외를 발생시키는 단일 기능 인터페이스가 작성됩니다 (CheckedValueSupplier ). 이것은 점검 된 예외를 허용하는 유일한 기능 인터페이스입니다. 다른 모든 기능 인터페이스 CheckedValueSupplier는를 사용하여 확인 된 예외를 발생시키는 코드를 래핑합니다.

그만큼 CheckedValue클래스는 확인이 끝난 예외를 throw 어떤 논리를 실행 한 결과를 개최한다. 이렇게하면 코드가 인스턴스에 CheckedValue포함 된 값에 액세스하려고 시도 할 때까지 확인 된 예외가 전파되지 않습니다 .

이 접근법의 문제점.

  • 우리는 "예외"를 던져서 원래 던져진 특정 유형을 효과적으로 숨 깁니다.
  • 우리 CheckedValue#get()는 호출 될 때까지 예외가 발생했다는 것을 알지 못합니다 .

소비자 외

일부 기능 인터페이스 (Consumer 예 :)는 반환 값을 제공하지 않으므로 다른 방식으로 처리해야합니다.

소비자 대신 기능

한 가지 접근 방식은 스트림을 처리 할 때 적용되는 소비자 대신 함수를 사용하는 것입니다.

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

차츰 오르다

또는 언제든지로 이관 할 수 있습니다 RuntimeException. 에서 확인 된 예외의 에스컬레이션을 다루는 다른 답변이 있습니다 Consumer.

소비하지 마십시오.

기능적인 인터페이스를 함께 피하고 좋은 for 루프를 사용하십시오.


2

unchecked()여러 유스 케이스를 처리 하는 오버로드 된 유틸리티 함수를 사용합니다 .


몇 가지 예

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

지원 유틸리티

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

0

제공된 솔루션 중 일부는 E의 일반 인수를 사용하여 예외 유형을 전달합니다.

한 단계 더 나아가 예외 유형을 전달하는 대신 예외 유형의 소비자를 다음과 같이 전달하십시오.

Consumer<E extends Exception>

재사용 가능한 여러 변형을 Consumer<Exception>만들어 응용 프로그램의 일반적인 예외 처리 요구를 충족시킬 수 있습니다.


0

나는 일반적인 것을 할 것입니다 :

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

용법:

 Lambda.handle(() -> method());

0

사용 Jool Library또는 말 jOOλ library에서JOOQ . 검사되지 않은 예외 처리 인터페이스를 제공 할뿐만 아니라 Seq 클래스에 유용한 메소드를 많이 제공합니다.

또한 최대 16 개의 매개 변수가있는 기능 인터페이스가 포함되어 있습니다. 또한 다양한 시나리오에서 사용되는 Tuple 클래스를 제공합니다.

줄 깃 링크

특히 org.jooq.lambda.fi.util.function패키지의 라이브러리 조회에서 . 여기에는 Checked가 붙은 Java-8의 모든 인터페이스가 포함됩니다. 참조를 위해 아래를보십시오 :-

여기에 이미지 설명을 입력하십시오


0

나는 어떤 자바 예외 를 잡거나 감싸지 않고도RuntimeException 어디서나 던질 수있는 일반적인 마술을 가진 작은 lib의 저자입니다 .

용법: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

출처 : https://github.com/qoomon/unchecked-exceptions-java


-7
public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

3
당신의 작업에 대해 언급 할 생각이 있습니까? 코드 전용 답변은 그렇게 유용하지 않습니다.
Phantômaxx

@ 프랭키 <code>/<code>: : 대신 4 간격을 사용하여 프레젠테이션을 수정할 수 있습니다.
AdrieanKhisbe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.