java - ratones - trampas para ratas con tubos pvc
¿Por qué no puedo tener una trampa para una excepción marcada para una llamada que arroja un genérico? (2)
Este comportamiento se especifica en la sección 11.2.3 de JLS :
Es un error en tiempo de compilación si una cláusula de captura puede detectar la clase de excepción comprobada
E1
y no es el caso que el bloque try correspondiente a la cláusula catch pueda arrojar una clase de excepción comprobada que sea una subclase o superclase deE1
, a menos queE1
Exception
o una superclase deException
.
MyCheckedException
ajusta a la descripción de la clase E1
anterior, porque no está declarado en la declaración genérica de processable.apply()
, y no es Exception
o una de sus superclases. El compilador solo sabe que el método puede arrojar un Throwable
, por lo que MyCheckedException
no se declara.
Estoy trabajando en un pequeño helper que se supone que invoque código arbitrario (pasado como lambda). El ayudante debería detectar ciertas excepciones y arrojarlas dentro de un envoltorio. Mis "propias" excepciones no deberían envolverse sino simplemente volverse a lanzar.
Se me ocurrió este código:
@FunctionalInterface
interface Processable<T, X extends Throwable> {
public T apply() throws X;
}
class MyCheckedException extends Exception { ... }
class MyCheckedExceptionWrapper extends MyCheckedException { ... }
public class MyExceptionLogger<T, X extends Throwable> {
public T process(Processable<T, X> processable) throws MyCheckedException {
try {
return processable.apply();
} catch (MyCheckedException thrown) { // this line isn''t accepted
throw thrown;
} catch (Exception | LinkageError thrown) {
throw new MyCheckedExceptionWrapper(thrown);
} catch (Throwable thrown) {
... just log
return null;
}
}
}
Lo anterior da un error de compilación:
Bloque catch inaccesible para MyCheckedException. Esta excepción nunca se lanza desde el cuerpo de la declaración try MyExceptionLogger ...
En otras palabras: aunque apply()
se define para lanzar algo, X extends Throwable
I no puede detectar una excepción específica marcada al invocar ese método.
Sé que puedo llegar al código de trabajo atrapando a Throwable , para luego usar una instanceof
verificación, pero me gustaría entender por qué no es posible hacer una prueba / captura como se describe anteriormente.
Creo que hay, basado en el JSL, una buena razón por la que no debería poder detectar su excepción marcada personalizada en su ejemplo.
Si lees la cita del JLS
Es un error en tiempo de compilación si una cláusula de captura puede detectar la clase de excepción comprobada
E1
y no es el caso que el bloque try correspondiente a la cláusula catch pueda arrojar una clase de excepción comprobada que sea una subclase o superclase deE1
, a menos queE1
Exception
o una superclase deException
.
Se debe permitir que una cláusula catch capture cualquier Exception
marcada, si un método en el try-block correspondiente declara Throwable
. En su ejemplo, el try-block puede arrojar una clase de excepción comprobada que sea una subclase o una superclase de MyCheckedException
concretamente MyCheckedException
y MyCheckedException
obviamente no es una Exception
o una superclase de Exception
Esto se puede verificar fácilmente eliminando los genéricos del ejemplo anterior y compilando sin problemas:
@FunctionalInterface
interface Processable<T> {
public T apply() throws Throwable;
}
private <T> T process(Processable<T> aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
Ese es el problema de alguna manera tiene que estar relacionado con el uso de genéricos en combinación con excepciones. Tal vez esto esté relacionado con el borrado de tipos, pero con los tipos borrados su ejemplo también funciona bien:
@FunctionalInterface
interface Processable {
public Object apply() throws Throwable;
}
private Object process(Processable aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}