Skip to content
Advertisement

argument mismatch; invalid functional descriptor for lambda expression

I am using the jdbi library like so:

try {
    return jdbi.withHandle(handle -> handle
        .createQuery("...")
        .mapTo(String.class)
        .one());
} catch (final JdbiException e) {
    if (DatabaseExceptionChecker.isTransientException(e)) {
        throw new MyCustomException("Transient database exception", e);
    } else {
        throw e;
    }
}

This try catch pattern is repeated a few times, with various different queries passed to the jdbi.withHandle method. Another example:

try {
    return jdbi.withHandle(handle -> handle
        .createUpdate("...")
        .execute());
} catch (final JdbiException e) {
    if (DatabaseExceptionChecker.isTransientException(e)) {
        throw new MyCustomException("Transient database exception", e);
    } else {
        throw e;
    }
}

The jdbi.withHandle method has this signature:

public <R, X extends Exception> R withHandle(HandleCallback<R, X> callback) throws X {

I am trying to find a way to reduce the duplication around the try-catch, so that I can use something like this instead:

handleTransientExceptions(() -> jdbi.withHandle(handle -> handle
        .createQuery("...")
        .mapTo(String.class)
        .one())
}

i.e. the handleTransientExceptions function would contain the try-catch boilderplate.

I’ve begun with the following:

@FunctionalInterface
private interface JdbiCall
{
    public <R, X extends Exception> R call() throws X;
}

public <R, X extends Exception> R handleTransientExceptions(final JdbiCall jdbiCall) throws MyCustomException
{
    try {
        return jdbiCall.call();
    } catch (final JdbiException e) {
        if (DatabaseExceptionChecker.isTransientException(e)) {
            throw new MyCustomException("Transient database exception", e);
        } else {
            throw e;
        }
    }
}

however, when I try to call it like this:

return handleTransientExceptions(() -> jdbi.withHandle(handle -> handle
        .createQuery("...")
        .mapTo(String.class)
        .one())
}

I get the error:

  reason: cannot infer type-variable(s) R,X
    (argument mismatch; invalid functional descriptor for lambda expression
      method <R,X>()R in interface com.acmecorp.MyService.JdbiCall is generic)

Answer

Types cannot be inferred, because your functional interface JdbiCall doesn’t have neither generics at the class declaration level nor parameters for call method by which these types can be inferred. So, your FI might look like this:

@FunctionalInterface
interface JdbiCall<R, X extends Exception> {
    R call() throws X;
} 

and handleTransientExceptions method:

public <R, X extends Exception> R handleTransientExceptions(final JdbiCall<R, X> jdbiCall) throws Exception {
    try {
        return jdbiCall.call();
    } catch (final JdbiException e) {
        if (DatabaseExceptionChecker.isTransientException(e)) {
            throw new MyCustomException("Transient database exception", e);
        } else {
            throw e;
        }
    }
}

Also it can be simplified to:

@FunctionalInterface
interface JdbiCall<R> {
    R call() throws Exception;
} 

handleTransientExceptions method:

public <R> R handleTransientExceptions(final JdbiCall<R> jdbiCall) throws Exception {
    try {
        return jdbiCall.call();
    } catch (final JdbiException e) {
        if (DatabaseExceptionChecker.isTransientException(e)) {
            throw new MyCustomException("Transient database exception", e);
        } else {
            throw e;
        }
    }
}
Advertisement