Java 8 : 람다 식에서 처리되는 필수 검사 예외. 왜 선택이 아닌 필수입니까?
저는 Java 8의 새로운 람다 기능을 가지고 놀면서 Java 8에서 제공하는 방법이 정말 유용하다는 것을 알았습니다. 그러나 다음 시나리오에 대한 해결 방법을 만드는 데 좋은 방법 이 있는지 궁금 합니다. 예를 들어 (사용 java.lang.functions.Factory
) 개체 풀을 채우기 위해 팩토리가 필요한 개체 풀 래퍼가 가정합니다 .
public class JdbcConnectionPool extends ObjectPool<Connection> {
public ConnectionPool(int maxConnections, String url) {
super(new Factory<Connection>() {
@Override
public Connection make() {
try {
return DriverManager.getConnection(url);
} catch ( SQLException ex ) {
throw new RuntimeException(ex);
}
}
}, maxConnections);
}
}
기능 인터페이스를 람다 식으로 변환하면 위의 코드는 다음과 가능합니다.
public class JdbcConnectionPool extends ObjectPool<Connection> {
public ConnectionPool(int maxConnections, String url) {
super(() -> {
try {
return DriverManager.getConnection(url);
} catch ( SQLException ex ) {
throw new RuntimeException(ex);
}
}, maxConnections);
}
}
그렇게 나쁘지는 실제로 않지만 확인 된 예외 java.sql.SQLException
에는 람다 내부에 try
/ catch
블록이 필요합니다 . 우리 회사에서는 오랫동안 두 가지 인터페이스를 사용합니다.
IOut<T>
이것은 다음과 가변적java.lang.functions.Factory
입니다.- 일반적으로 확인 된 예외 전파가 필요한 경우에 대한 특수 인터페이스 :
interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }
.
IOut<T>
및 둘 다 IUnsafeOut<T>
Java 8로 마이그레이션하는 동안 제거되어야하지만 정확히 일치하는 IUnsafeOut<T, E>
. 람다식이 확인되지 않은 것으로 확인 된 예외를 처리 할 수 있습니다. 위의 생성자에서 다음과 같이 간단하게 사용할 수 있습니다.
super(() -> DriverManager.getConnection(url), maxConnections);
훨씬 깔끔해. ObjectPool
수퍼 클래스를 다시 작성하여 를 수락 할 수 있습니다 IUnsafeOut<T>
아직 완료 내가 아는 한 Java 8은 아직 완료되지 않았거나 다음과 같은 몇 가지 변경 사항이있을 수 있습니다.
- 꾸미는 것을 구현
IUnsafeOut<T, E>
? (솔직히 말해서, 나는 그것이 더럽다고 생각합니다. 대상은 어떤 것을 받아 들일지 선택해야하는지 :Factory
호환 가능한 방법 서명을 필요로하지 않는 "안전하지 않은 공장"또는 "안전하지 않은 공장") - 람다에서 확인 된 예외를 무시
IUnsafeOut<T, E>
하고 게이트 가 필요하지 않습니다. (왜 안되어야합니까? 예를 들어 또 다른 중요한 변경 사항 : 제가 사용하는 OpenJDKjavac
는 이제final
익명 클래스 [기능적 인터페이스] 또는 람다에서 선택할 변수와 변수를 요구하지 않습니다. )
따라서 질문은 일반적으로 람다에서 확인 된 예외를 우회 할 수있는 방법이 있습니까? 아니면 Java 8이 최종적으로 출시 될 때까지 유효한 계획이 있습니까?
업데이트 1
흠, 내가 현재 가지고있는 것을 이해하는 한, 참조 된 가이드가 2010 년에 작성 방법이 있음에도 불구하고 지금은이없는 것입니다. Brian Goetz가 Java의 예외를 투명하게 설명합니다 . Java 8에서 많이 변경된 것이 이것이 답으로 될 수 있습니다. 또한 Brian은 interface ExceptionalCallable<V, E extends Exception>
( IUnsafeOut<T, E extends Throwable>
우리 코드 레거시에서 많은 한 ) 거의 쓸모가 말하고 나는 그에게 동의합니다.
여전히 다른 것을 놓치고 있습니까?
내가 당신의 질문에 대답 할 수 있을지 모르겠지만, 당신은 그런 그런 것을 사용할 수 없습니까?
public final class SupplierUtils {
private SupplierUtils() {
}
public static <T> Supplier<T> wrap(Callable<T> callable) {
return () -> {
try {
return callable.call();
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
public class JdbcConnectionPool extends ObjectPool<Connection> {
public JdbcConnectionPool(int maxConnections, String url) {
super(SupplierUtils.wrap(() -> DriverManager.getConnection(url)), maxConnections);
}
}
람다 메일 링리스트에서 논의 히 논의되었습니다 . 보시다시피 Brian Goetz는 대안이 자신의 조합 작성하는 제안했습니다.
또는 자신만의 사소한 결합 사용 가능합니다.
static<T> Supplier<T> exceptionWrappingSupplier(Supplier<T> b) { return e -> { try { b.accept(e); } catch (Exception e) { throw new RuntimeException(e); } }; }
이메일 이메일을 작성하는 데 시간보다 더 적게 한 번만 있습니다. 사용하는 각 SAM 유형에 대해 사용하는 것이 좋습니다.
나는 대안 대안 이라기보다는 "유리 99 %가 가득 찬"것으로보고 싶습니다. 모든 문제에 대한 새로운 언어 기능이 솔루션으로 필요한 것은 아닙니다. (새로운 언어 기능이 항상 새로운 문제를 일으킨다는 것은 말할 것도 없습니다.)
모든 소비자 인터페이스는 블록이라고 불 렸습니다.
나는 JB Nizet의 대답 과 일치라고 생각합니다 .
나중에 Brian 이 이것이 왜 이렇게 설계했는지 설명합니다 .
예, 고유 한 뛰어난 SAM을 제공해야합니다. 그러나 람다 변환은 그들과 잘 작동합니다.
EG는이 문제에 대한 추가 언어 및 라이브러리 지원에 대해 논의 할 것이 결국 나쁜 비용 / 이익 충이라고합니다.
라이브러리 기반 솔루션은 기본 전문화를 기존 조합 폭발과 나쁘게 상호 작용하는 SAM 유형 (예외 대 비)에서 2 배의 폭발을 일으.
사용 가능한 언어 기반 솔루션은 표준 / 가치 절충으로 실패했습니다. 몇 가지 대체 솔루션이 계속해서 탐구 할 것입니다. 분명히 8은 아니고 아마도 9는 아닐 것입니다.
그동안 원하는 작업을 수행 할 수있는 도구가 있습니다. 우리가 마지막 마일을 제공하는 것을 선호한다는 것을 알고 있습니다 (그리고 두 번째로, 귀하의 요청은 "이미 확인 된 예외를 제공하지 않는 이유"에 대한 얕은 요청입니다). 일을 완료합니다.
2015 년 9 월 :
이를 위해 ET 를 사용할 수 있습니다 . ET는 예외 변환 / 번역을위한 작은 Java 8 라이브러리입니다.
ET를 사용하면 다음과 같이 사용할 수 있습니다.
super(() -> et.withReturningTranslation(() -> DriverManager.getConnection(url)), maxConnections);
다중 라인 버전 :
super(() -> {
return et.withReturningTranslation(() -> DriverManager.getConnection(url));
}, maxConnections);
이전에해야 할 일 새 ExceptionTranslator
인스턴스를 만드는 것입니다 .
ExceptionTranslator et = ET.newConfiguration().done();
이 인스턴스는 여러 가지 구성 요소에서 공유 할 수 있습니다. 원하는 경우보다 구체적인 예외 변환 규칙 (예 :)을 구성 할 수 있습니다 FooCheckedException -> BarRuntimeException
. 다른 규칙을 사용할 수없는 경우 예외는 자동으로 변환 RuntimeException
됩니다.
(면책 조항 : 나는 ET의 저자입니다)
RuntimeException (확인되지 않음) 래퍼 클래스를 사용하여 람다 식에서 원래 예외를 밀수 한 다음 래핑 된 예외 를 원래 확인 된 예외로 다시 캐스팅하는 것을 고려해 보 입니까?
class WrappedSqlException extends RuntimeException {
static final long serialVersionUID = 20130808044800000L;
public WrappedSqlException(SQLException cause) { super(cause); }
public SQLException getSqlException() { return (SQLException) getCause(); }
}
public ConnectionPool(int maxConnections, String url) throws SQLException {
try {
super(() -> {
try {
return DriverManager.getConnection(url);
} catch ( SQLException ex ) {
throw new WrappedSqlException(ex);
}
}, maxConnections);
} catch (WrappedSqlException wse) {
throw wse.getSqlException();
}
}
고유 한 클래스를 생성하면 예외가 포착하여 다시 던지기 전에 파이프 라인의 어딘가에 직렬화 되더라도 람다 내에서 래핑 한 예외에 대해 확인되지 않은 다른 예외를 잘못 인식 할 가능성을 방지해야합니다.
흠 ... 여기서 문제가되는 유일한 점은 생성자 내에서 super () 호출을 사용하여 생성자 내에서이 작업을 수행하고 있다는 것입니다. 이는 법에 따라 생성자의 첫 번째 문이어야합니다. try
이전 진술로 간주 됩니까 ? 내 코드에서 생성자없이 작동합니다.
우리는이를 도와 준 회사 내부 프로젝트를 개발했습니다. 우리는 두 달 전에 공개하기로 결정했습니다.
이것이 우리가 생각 해낸 것입니다.
@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;
/**
* @param <T> type
* @param <E> checked exception
* @return a function that accepts one argument and returns it as a value.
*/
static <T, E extends Exception> ThrowingFunction<T, T, E> identity() {
return t -> t;
}
/**
* @return a Function that returns the result of the given function as an Optional instance.
* In case of a failure, empty Optional is returned
*/
static <T, R, E extends Exception> Function<T, Optional<R>> lifted(ThrowingFunction<T, R, E> f) {
Objects.requireNonNull(f);
return f.lift();
}
static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
Objects.requireNonNull(f);
return f.uncheck();
}
default <V> ThrowingFunction<V, R, E> compose(final ThrowingFunction<? super V, ? extends T, E> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> ThrowingFunction<T, V, E> andThen(final ThrowingFunction<? super R, ? extends V, E> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* @return a Function that returns the result as an Optional instance. In case of a failure, empty Optional is
* returned
*/
default Function<T, Optional<R>> lift() {
return t -> {
try {
return Optional.of(apply(t));
} catch (Throwable e) {
return Optional.empty();
}
};
}
/**
* @return a new Function instance which wraps thrown checked exception instance into a RuntimeException
*/
default Function<T, R> uncheck() {
return t -> {
try {
return apply(t);
} catch (final Throwable e) {
throw new WrappedException(e);
}
};
}
}
https://github.com/TouK/ThrowingFunction/
설명 된 방식으로 예외를 래핑하면 작동하지 않습니다. 시도했지만 실제로 사양에 따라 컴파일러 오류가 발생합니다. 람다식이 메서드 인수의 대상 유형과 호환되지 않는 예외를 throw합니다. Callable; call ()은 그것을 던지지 않으므로 람다 식을 Callable로 전달할 수 없습니다.
따라서 기본적으로 해결책은 없습니다. 우리는 상용구를 작성해야합니다. 우리가 할 수있는 유일한 일은 이것이 수정이 필요하다는 의견을 표명하는 것입니다. 사양은 호환되지 않는 throw 된 예외를 기반으로 대상 유형을 맹목적으로 폐기해서는 안된다고 생각합니다. 이후에 throw 된 호환되지 않는 예외가 호출 범위에서 throw로 선언되었는지 여부를 확인해야합니다. 그리고 인라인되지 않은 람다 식의 경우 자동으로 확인 된 예외를 throw하는 것으로 표시 할 수 있다고 제안합니다 (컴파일러가 확인해서는 안되지만 런타임은 여전히 포착해야한다는 의미에서 조용함). ->이 사이트가 토론 사이트가 아니라는 것을 알고 있지만, 이것이 질문에 대한 유일한 해결책이기 때문에 자신을 들어보고이 사양을 변경합시다!
Paguro는 확인 된 예외를 래핑하는 기능적 인터페이스를 제공합니다 . 질문을 한 지 몇 달 후부터 작업을 시작 했으므로 아마도 영감을 얻었을 것입니다!
Paguro에는 4 개의 기능 인터페이스와 Java 8에 포함 된 43 개의 인터페이스 만 있다는 것을 알 수 있습니다. Paguro는 기본 형식보다 제네릭을 선호하기 때문입니다.
Paguro에는 변경 불가능한 컬렉션 (Clojure에서 복사)에 내장 된 단일 패스 변환이 있습니다. 이러한 변환은 Clojure 변환기 또는 Java 8 스트림과 거의 동일하지만 확인 된 예외를 래핑하는 기능 인터페이스를 허용합니다. 참조 : Paguro와 Java 8 스트림의 차이점 .
람다에서 던질 수 있습니다. "자신 만의 방식으로"선언해야합니다 (안타깝게도 표준 JDK 코드에서는 재사용 할 수 없지만 우리는 할 수있는 일을합니다).
@FunctionalInterface
public interface SupplierIOException {
MyClass get() throws IOException;
}
또는 좀 더 일반적인 버전 :
public interface ThrowingSupplier<T, E extends Exception> {
T get() throws E;
}
여기 심판 . 또한 "sneakyThrow"를 사용하여 확인 된 예외를 선언하지 않고 계속 던진다는 언급이 있습니다. 내 머리가 조금 아파요, 아마도 옵션 일 수 있습니다.
'ProgramingTip' 카테고리의 다른 글
maven "기호를 제거 할 수 없음"오류가 없습니다. (0) | 2020.11.05 |
---|---|
ʻint foo :: * bar :: *`는 무엇입니까? (0) | 2020.11.05 |
WPF에서 "마우스 기지"란 무엇을 의미합니까? (0) | 2020.11.05 |
gdb- 파이프 입력 (인수 아님)으로하지 (0) | 2020.11.05 |
Python : __builtin__과 __builtins__의 차이점은 무엇입니까? (0) | 2020.11.05 |