[英]Java type inference of generic exception type
我正在嘗試使用一個可能拋出多個異常的仿函數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);
}
}
直觀地說,我希望上面的例子能夠編譯,但事實並非如此。 有沒有辦法讓這個工作,或者是唯一的方法指定類型參數的解決方法? 編譯錯誤是:
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
Java解析器有一個奇怪的特性(在jdk 1.8u152和9.0.1中,但不是 Eclipse內置的編譯器)所以當你有
@FunctionalInterface
interface SQLExceptionThrowingFunction<T, U, E extends Exception> {
U apply(T t) throws E, SQLException;
}
然后傳遞Test::<SQLException>mayThrow1
它在創建接口實例時將E綁定到SQLException。
你可以把它這樣做,只需在界面交換的聲明異常,即只是做
@FunctionalInterface
interface SQLExceptionThrowingFunction<T, U, E extends Exception> {
U apply(T t) throws SQLException, E;
}
然后編譯!
JLS的相關部分是第18.2.5節 。 但我無法看到它解釋上述行為的位置。
對不起我的評論,它實際上沒有編譯,但它以某種方式在Eclipse上運行。 我認為編譯錯誤實際上是預期的。 調用方法的簽名是:
public static <T, E extends Exception> T call(Class<E> exceptionClass, SQLExceptionThrowingFunction<Connection, T, E> f) throws E
你正在使用它:
ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1);
通過該方法的簽名,第一個參數(RuntimeException)的類必須與mayThrow1(SQLException)的泛型匹配,因為它們在簽名中都是E.
當你看到public static <T, E extends Exception> T call(Class<E> exceptionClass, SQLExceptionThrowingFunction<Connection, T, E> f) throws E
告訴:
Class<E>
的E
類型(您的第一個參數,exceptionClass)和 SQLExceptionThrowingFunction<Connection, T, E> f) throws E
的E
類型SQLExceptionThrowingFunction<Connection, T, E> f) throws E
應具有相同的類型/子類型。
因此, E
(即SQLException
中) SQLExceptionThrowingFunction
預計的亞型E
(exceptionClass),其作為傳遞RuntimeException
)。 (當你調用ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1);
時會發生這種情況ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1);
由於此期望失敗,您將收到編譯錯誤。
您可以通過更改來驗證這一點
ConnectionPool.call(RuntimeException.class, Test::<SQLException>mayThrow1);
至 ConnectionPool.call(
Exception .class, fitest.Test::<SQLException>mayThrow1);
這將刪除該行上的錯誤。 不確定這是否是您最初的意圖。
1:你可以做些什么來使用通用的東西(如果你不關心聲明異常是改變調用方法,如下所示,那么你的所有代碼都可以工作。
public static <T> T call2(Class exceptionClass, SQLExceptionThrowingFunction<Connection,T, Exception> f)
{
throw new UnsupportedOperationException("unimportant");
}
2:或者你可以在沒有定義類型的情況下調用。 例如
ConnectionPool.call(RuntimeException.class, Test::mayThrow0);
ConnectionPool.call(Checked.class, Test::mayThrow1);
我不確定這是否能解決你的問題。 如果你有不同的意圖,當你說Is there a way to get this to work, or is the workaround of specifying the type arguments the only way
實際上你想要Is there a way to get this to work, or is the workaround of specifying the type arguments the only way
然后請分享pseduo語法你想如何工作的東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.