Java8 Lambdas y excepciones

5 minutos de lectura

avatar de usuario
rompetroll

Me pregunto si alguien podría explicarme la siguiente rareza. Estoy usando la actualización 11 de Java 8.

Dado este método

private <F,T> T runFun(Function<Optional<F>, T> fun, Optional<F> opt) {
   return fun.apply(opt) ;
}

Si primero construyo un objeto de función y lo paso al método anterior, las cosas se compilan.

private void doesCompile() {
    Function<Optional<String>, String> fun = o -> o.orElseThrow(() -> new RuntimeException("nah"));
    runFun(fun, Optional.of("foo"));

}

Pero, si alineo la función como una lambda, el compilador dice

excepción no reportada X; debe ser atrapado o declarado para ser lanzado

private void doesNotCompile () {
    runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
}

Actualizar: Resulta que el mensaje de error fue abreviado por maven. Si se compila directamente con javac, el error es:

error: unreported exception X; must be caught or declared to be thrown
            runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
                                     ^
  where X,T are type-variables:
    X extends Throwable declared in method <X>orElseThrow(Supplier<? extends X>)
    T extends Object declared in class Optional

Ver también aquí para el código de prueba ejecutable.

  • Suena como otro tipo de error de inferencia. Asegúrese de incluir información detallada sobre el compilador obtiene este error ya que Eclipse usa el suyo propio.

    – Marko Topolnik

    27 de agosto de 2014 a las 9:27

  • observe que sucede lo mismo si usa una clase anónima, por lo que no es un problema de lambda.

    – usuario2504380

    27 de agosto de 2014 a las 10:38

  • Un aparte: Eclipse compila esto muy bien, y usando o.<RuntimeException>orElseThrow(()... resuelve el problema, sin embargo, esto no debería ser necesario. (Creo que se informó un problema similar en otra pregunta de stackoverflow hace un tiempo, pero no lo encontré en este momento…)

    – marco13

    27 de agosto de 2014 a las 11:42

  • “mvn -e” puede ser útil.

    – Thorbjorn Ravn Andersen

    25 de septiembre de 2015 a las 8:49

avatar de usuario
Keisar

Esto es lo que resolvió el problema para mí:

en lugar de escribir

optional.map(this::mappingFunction).orElseThrow(() -> new BadRequestException("bla bla"));

Escribí:

optional.map(this::mappingFunction).<BadRequestException>orElseThrow(() -> new BadRequestException("bla bla"));

Agregar lo explícito <BadRequestException> ayuda con estos casos de borde lambda (que son bastante molestos…)

ACTUALIZACIÓN: Esto es en caso de que no pueda actualizar a la última versión de JDK, si puede, debería…

  • Estoy en la última versión de Java y sigo recibiendo un error. .getOrElseThrow(throwable me ayudó. Gracias.

    -Hans Wurst

    28 de septiembre de 2017 a las 11:14

  • Esto ayudó, gracias, no pude actualizar porque no soy dueño de la máquina.

    – Alex

    17 oct 2017 a las 14:01

  • Estoy en 1.8.0_144, todavía tenía que usar su solución. Eso ayudo

    – olyv

    18/10/2017 a las 15:32

  • Oracle 1.8.0_171 también está roto.

    – Aarón Digulla

    23 de julio de 2018 a las 10:01

  • 1.8.0_181 también necesita el reparto explícito.

    – Sven

    16 de mayo de 2019 a las 5:08

Esto parece un caso de error JDK-8054569que no afecta a Eclipse.

Pude reducirlo reemplazando Función con Proveedor y extrayendo el orElseThrow método:

abstract <T> void f(Supplier<T> s);

abstract <T, X extends Throwable> T g(Supplier<? extends X> x) throws X;

void bug() {
    f(() -> g(() -> new RuntimeException("foo")));
}

y luego eliminando los proveedores y las lambdas por completo:

abstract <T> void f(T t);

abstract <T, X extends Throwable> T g(X x) throws X;

void bug() {
    f(g(new RuntimeException("foo")));
}

que en realidad es un ejemplo más limpio que el del informe de error. Esto muestra el mismo error si se compila como Java 8, pero funciona bien con -source 1.7.

Supongo que pasar un tipo de retorno de método genérico a un parámetro de método genérico hace que la inferencia de tipo para la excepción falle, por lo que asume que el tipo es Throwable y se queja de que este tipo de excepción verificado no se maneja. El error desaparece si declaras bug() throws Throwable o cambiar el límite a X extends RuntimeException (así que está desmarcado).

  • Actualizado de 1.8.0_45 a 1.8.0_92 y resolvió mis problemas. Gracias

    – brazo

    16 mayo 2016 a las 20:08

Si está intentando compilar el proyecto de otra persona, intente actualizar a 1.8.0_92

  • ¿Está seguro? Todavía tengo este error en _101 y otras personas reportan este error en _171

    – Pieter De Bié

    9 de noviembre de 2018 a las 12:41

Similar a @keisar, podría resolver mi problema (ver maven-compiler-plugin no puede compilar un archivo con el que Eclipse no tiene problemas) especificando el parámetro de tipo.

Sin embargo, lo encontré mucho más conveniente (ya que uso NotFoundException en varios lugares) para simplemente hacer que mi clase de excepción sea propia Supplier:

public class NotFoundException extends RuntimeException
    implements Supplier<NotFoundException> {

    // Rest of the code

    @Override
    public NotFoundException get() {
        return this;
    }

}

Entonces simplemente puedes hacer:

// Distribution.rep().get(id) returns a java.util.Optional
Distribution distro = Distribution.rep().get(id).orElseThrow(
    new NotUniqueException("Exception message"));

Si agrega explícitamente <RunTimeException> se ve feo, entonces como una solución que puede reemplazar con orElseGet()

.orElseGet(() -> throw new RunTimeException("foo"));

  • No creo que puedas lanzar una excepción en línea como esa… la declaración de lanzamiento debe estar en un bloque.

    – Xavier Dury

    31 de marzo de 2021 a las 13:48

  • No creo que puedas lanzar una excepción en línea como esa… la declaración de lanzamiento debe estar en un bloque.

    – Xavier Dury

    31 de marzo de 2021 a las 13:48

¿Ha sido útil esta solución?