Mientras escribía código para otra respuesta en este sitio, encontré esta peculiaridad:
static void testSneaky() {
final Exception e = new Exception();
sneakyThrow(e); //no problems here
nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception
}
@SuppressWarnings("unchecked")
static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
static <T extends Throwable> void nonSneakyThrow(T t) throws T {
throw t;
}
Primero, estoy bastante confundido por qué la sneakyThrowllamada está bien para el compilador. ¿Qué tipo posible dedujo Tcuando no se menciona en ninguna parte un tipo de excepción sin marcar?
En segundo lugar, aceptando que esto funciona, ¿por qué entonces el compilador se queja en la nonSneakyThrowllamada? Se parecen mucho.
fuente

sneakyThrowllamada. Las regulaciones especiales sobre la inferencia dethrows Tformas no existían en la especificación de Java 7.nonSneakyThrow,Tdebe serException, no "un supertipo de"Exception, porque es exactamente el tipo de argumento declarado en tiempo de compilación en el sitio de llamada.Exceptiony un límite superiorThrowable, por lo que el límite superior mínimo, que es el tipo inferido resultante, esException.Exceptionfrase "o sí mismo" puede ser útil para el lector, pero en general, debe tenerse en cuenta que la especificación siempre usa los términos "subtipo" y "supertipo" en el sentido de "incluirse a sí mismo" ...Si la inferencia de tipo produce un único límite superior para una variable de tipo, normalmente se elige el límite superior como solución. Por ejemplo, si
T<<Numberla solución esT=Number. AunqueInteger,Floatetc. también podrían satisfacer la restricción, no hay una buena razón para elegirlosNumber.Ese fue también el caso para
throws Ten Java 5-7:T<<Throwable => T=Throwable. (Todas las soluciones de lanzamiento furtivo tenían<RuntimeException>argumentos de tipo explícitos , de lo contrario<Throwable>se infiere).En java8, con la introducción de lambda, esto se vuelve problemático. Considere este caso
interface Action<T extends Throwable> { void doIt() throws T; } <T extends Throwable> void invoke(Action<T> action) throws T { action.doIt(); // throws T }Si invocamos con una lambda vacía, ¿cómo se
Tinferiría?La única restricción
Tes un límite superiorThrowable. En una etapa anterior de java8,T=Throwablese inferiría . Vea este informe que presenté.Pero eso es bastante tonto, inferir
Throwable, una excepción marcada, de un bloque vacío. En el informe se propuso una solución (que aparentemente fue adoptada por JLS):If E has not been inferred from previous steps, and E is in the throw clause, and E has an upper constraint E<<X, if X:>RuntimeException, infer E=RuntimeException otherwise, infer E=X. (X is an Error or a checked exception)es decir, si el límite superior es
ExceptionoThrowable, elijaRuntimeExceptioncomo solución. En este caso, no es una buena razón para elegir un subtipo particular de la cota superior.fuente
X:>RuntimeExceptionen su último fragmento de ejemplo?Con
sneakyThrow, el tipoTes una variable de tipo genérico acotado sin un tipo específico (porque no hay de dónde podría provenir el tipo).Con
nonSneakyThrow, el tipoTes del mismo tipo que el argumento, por lo que en su ejemplo,TdenonSneakyThrow(e);esException. ComotestSneaky()no declara un arrojadoException, se muestra un error.Tenga en cuenta que se trata de una interferencia conocida de los genéricos con las excepciones marcadas.
fuente
sneakyThrow, ¿no se infiere realmente de ningún tipo específico, y el "elenco" es de un tipo tan indefinido? Me pregunto qué pasa realmente con esto.