我正在尝试使用一个可能抛出多个异常的仿函数F(在下面的示例中,Checked和SQLException).我希望能够使用F作为参数调用一个函数,这样任何检查的异常F抛出(除了将在内部处理的SQLException)都会被重新抛出.
import java.sql.Connection;
import java.sql.SQLException;
class Checked extends Exception {
public Checked() {
super();
}
}
@FunctionalInterface
interface SQLExceptionThrowingFunction<T, U, E extends Exception> {
U apply(T t) throws E, SQLException;
}
class ConnectionPool {
public static <T, E extends Exception> T call(Class<E> exceptionClass, SQLExceptionThrowingFunction<Connection, T, E> f) throws E {
throw new UnsupportedOperationException("unimportant");
}
}
class Test {
static Void mayThrow0(Connection c) {
throw new UnsupportedOperationException("unimportant");
}
static <E extends Exception> Void mayThrow1(Connection c) throws E {
throw new UnsupportedOperationException("unimportant");
}
static <E1 extends Exception, E2 extends Exception> Void mayThrow2(Connection c) throws E1, E2 {
throw new UnsupportedOperationException("unimportant");
}
public static void main(String[] args) throws Exception {
// Intended code, but doesn't compile
ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1);
ConnectionPool.call(Checked.class, Test::<Checked, SQLException>mayThrow2);
// Type inference works if the function doesn't actually throw SQLException (doesn't help me)
ConnectionPool.call(RuntimeException.class, Test::mayThrow0);
ConnectionPool.call(Checked.class, Test::<Checked>mayThrow1);
// Can workaround by manually specifying the type parameters to ConnectionPool.call (but is tedious)
ConnectionPool.<Void, RuntimeException>call(RuntimeException.class, Test::<SQLException>mayThrow1);
ConnectionPool.<Void, Checked>call(Checked.class, Test::<Checked, SQLException>mayThrow2);
}
}
Run Code Online (Sandbox Code Playgroud)
直观地说,我希望上面的例子能够编译,但事实并非如此.有没有办法让这个工作,或者是唯一的方法指定类型参数的解决方法?编译错误是:
Test.java:34: error: incompatible types: inference variable E has incompatible bounds
ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1); // doesn't compile
^
equality constraints: RuntimeException
lower bounds: SQLException
where E,T are type-variables:
E extends Exception declared in method <T,E>call(Class<E>,SQLExceptionThrowingFunction<Connection,T,E>)
T extends Object declared in method <T,E>call(Class<E>,SQLExceptionThrowingFunction<Connection,T,E>)
Test.java:35: error: incompatible types: inference variable E has incompatible bounds
ConnectionPool.call(Checked.class, Test::<Checked, SQLException>mayThrow2); // doesn't compile
^
equality constraints: Checked
lower bounds: SQLException,Checked
where E,T are type-variables:
E extends Exception declared in method <T,E>call(Class<E>,SQLExceptionThrowingFunction<Connection,T,E>)
T extends Object declared in method <T,E>call(Class<E>,SQLExceptionThrowingFunction<Connection,T,E>)
2 errors
Run Code Online (Sandbox Code Playgroud)
Java解析器有一个奇怪的特性(在jdk 1.8u152和9.0.1中,但不是 Eclipse内置的编译器)所以当你有
@FunctionalInterface
interface SQLExceptionThrowingFunction<T, U, E extends Exception> {
U apply(T t) throws E, SQLException;
}
Run Code Online (Sandbox Code Playgroud)
并且Test::<SQLException>mayThrow1当它创建接口的实例时,它传递它将E绑定到SQLException.
你可以把它不能做到这一点,只需在界面交换的声明异常,即只是做
@FunctionalInterface
interface SQLExceptionThrowingFunction<T, U, E extends Exception> {
U apply(T t) throws SQLException, E;
}
Run Code Online (Sandbox Code Playgroud)
然后编译!
JLS的相关部分是第18.2.5节.但我无法看到它解释上述行为的位置.