Considere el siguiente artículo en JLS §18.1.3 - Límites
Aquí, cuando tratamos de identificar el conjunto de límites en las variables de inferencia, tenemos una de las siguientes situaciones:
...
- throws α: La variable de inferencia α aparece en una cláusula throws.
...
Un límite de la forma throws α es puramente informativo: dirige la resolución para optimizar la creación de instancias de α para que, si es posible, no sea un tipo de excepción comprobada .
Creo que esta afirmación es incorrecta:
¿Es correcto mi entendimiento aquí o me estoy perdiendo algo?
Creo que su interpretación/comprensión de esta declaración es un poco equivocada:
Un límite de la forma throws α es puramente informativo: dirige la resolución para optimizar la creación de instancias de α para que, si es posible, no sea un tipo de excepción comprobada.
Esa línea se refiere a la resolución, que, según tengo entendido, no se trata de dónde throws α , sino de dónde se infiere α , posiblemente la invocación del método.
Considere esta clase:
static class MyClass { public static void main(String[] args) { MyClass.<RuntimeException>something(0); // same as MyClass.something(1); try { MyClass.<IOException>something(2); } catch (IOException ex) { // checked exception } } /** * Will throw IOException if argument is 2, a RuntimeException otherwise */ static <T extends Exception> void something(int a) throws T { if (a == 2) { throw (T) new IOException(); //of course it's a bad cast } else { throw (T) new Exception(); } } } Después de analizar el método de dos something , concéntrese en la invocación en el método main :
La llamada MyClass.<IOException>something(0) espera una IOException. La persona que llama lo sabe (suponga un contrato completamente documentado en lugar de un código estrechamente acoplado) y maneja la excepción.
Esto ya te dice que la variable puede ser una excepción comprobada, al contrario de lo que piensas.
Por el contrario, la llamada MyClass.<RuntimeException>something(0) espera una excepción de tiempo de ejecución por motivos similares.
La forma en que se infiere α ( T en el ejemplo anterior) permite que el compilador omita obligar a la persona que llama a capturar/manejar la excepción (si es para ver el límite, que de otro modo tendría que hacerlo)
Ahora sobre la "optimización": se puede esperar razonablemente que la variable de tipo que se limita a medida que extends Exception se resuelva en una excepción marcada. Pero, si la persona que llama sabe que será una excepción de tiempo de ejecución, puede "informar" al compilador que será una excepción de tiempo de ejecución. Esto es lo que hice al especificar RuntimeException en el testigo de tipo ( RuntimeException también es el tipo resuelto cuando no se proporciona explícitamente ningún argumento de tipo).
Podemos pasar días para interpretar la "optimización", pero al menos yo, como llamador, no tuve que intentar/atrapar la invocación, y aún así no molesté al compilador (primera invocación).
Considere este ejemplo:
public class Main { public static void main(String[] args) { foo(() -> System.out.println("Foo")); } public static <T extends Exception> void foo(ThrowingRunnable<T> runnable) throws T { runnable.run(); } } interface ThrowingRunnable<T extends Exception> { void run() throws T; } Durante la inferencia del parámetro de tipo T al llamar a foo , habrá un límite de "lanzamientos" en la variable de tipo T , y se infiere que T es RuntimeException . Si no fuera por este límite, T se habría inferido como Exception debido a que T extends Exception . Esto habría significado que necesitaba hacer:
try { foo(() -> System.out.println("Foo")); catch (Exception e) { // This won't be reached! }¡Tuve que manejar la excepción, aunque todo lo que hago en la lambda es imprimir cosas! Eso no parece muy agradable, ¿verdad?
El propósito de este límite es que si no hay razón para que el método arroje una excepción verificada, no arroje una excepción verificada, de modo que no tenga que escribir tantos intentos... capturas por todas partes. . foo solo arrojaría una excepción comprobada si hace cosas como:
foo(() -> new FileOutputStream("foo")); Si el efecto del límite fuera forzar a T a ser una excepción comprobada, no sería muy útil.