3

First, I should say that I am able to get the generic type of instances or anonymous inner classes.

Here is the code example, look for the catch block:

public static interface MyInterface<E extends Throwable>{
    void call() throws E;
}


public static <E extends Throwable> void method(MyInterface<E> lambda) throws E {
    try {
        lambda.call();
    } catch(Throwable ex) {
        // Pseudo code                                   
        Class<?> T = ((ParameterizedType)ex.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]; 
        if ( T.isInstance(ex) ) {
            throw ex;
        } else {
             ... 
        }                                                  
    }
}

public static void throwsException() throws Exception {
    throw new Exception();
}

This doesn't work:

method(() -> {
    throwsException();
});

But this does:

method(new MyInterface<Exception>() {
    @Override
    public void call() throws Exception {
        throwsException();
    }
});

But with the introduction of Lambdas, i can no longer enforce this!

It should also be noted that this now potentially breaks backwards compatibility with Libraries older than < 8, and that reflected out this information.

I have researched this topic, and there only seems to be possible workarounds for getting the method parameters, but this is regarding the throws part so that won't work.

Note: I have seen this thread already: Reflection type inference on Java 8 Lambdas

9
  • 2
    I don't see this as backwards compatibility issue. getGenericInterfaces works by returning what appears in the source code. With lambdas, you don't have an actual type argument. Commented Dec 18, 2014 at 16:00
  • 3
    Remember that generic types are a compile time concept, they don't exist at runtime. What you're getting through getGenericInterfaces is what appears in the source code (ie. compile time), not the type that was potentially inferred at some method invocation (they they could sometimes be the same). Commented Dec 18, 2014 at 17:30
  • 1
    Except not really. getGenericInterfaces only works with direct implementations. Your solution will fail if you add a level of indirection, ie. an extra interface or subtype in between the instance type and the MyInterface interface. It will also fail if you provide a Proxy instance that implements MyInterface. Your parameter type MyInterface<E> can only restrict the type of the expression. It cannot restrict how that type was generated, whether compile time or runtime. Commented Dec 18, 2014 at 20:19
  • 1
    Sorry for code in comments, but I want to get my point across if it hadn't already. Say you have, for whatever reason, interface Some extends MyInterface<Exception> and class SomeImpl implements Some. You can use method(new SomeImpl());, but the getGenericSuperclass "hack" will not work. Commented Dec 18, 2014 at 22:01
  • 1
    Euh, I mean getGenericInterfaces rather than getGenericSuperclass for this case. You might want to look into type tokens and why/how they work too. Commented Dec 18, 2014 at 22:26

1 Answer 1

4

You can simply write

public static interface MyInterface<E extends Throwable>{
    void call() throws E;
}

public static <E extends Throwable> void method(MyInterface<E> lambda) throws E {
    try {
        lambda.call();
    }
    catch(Throwable ex) {
        throw ex;
    }
}

without dealing with Reflection. Since MyInterface<E>.call() is guaranteed to throw types assignable to E or unchecked exceptions only, it is clear that the catched ex must be either of a type assignable to E or an unchecked exception, hence may be safely re-thrown.

This works since Java 7.


In contrast, if your method performs other operations which may throw exceptions not guaranteed to be compatible with E, you should declare them explicitly rather than doing Reflection magic. Otherwise you have a method changing its behavior in a strange way as it depends then on the parameter whether a particular exception type is simply thrown or handled differently (wrapped or whatever your fall-back behavior is).

Note that your Reflection magic is broken anyway. Consider this simple example:

class Foo<E extends Throwable> implements MyInterface<E> {
    public void call() throws E {
    }
}

when instantiating this class directly, you can’t get the actual type arguments at runtime via Reflection. This is exactly the same behavior as with implementations created via lambda expressions.

Sign up to request clarification or add additional context in comments.

9 Comments

Well, your second argument is my case. I am writing an API and would like to handle and then wrap all other exceptions not explicitly declared and expected to be rethrown. All other exceptions I would like to wrap in a second exception so the caller can easily catch on one or the other. It is not completely useless scenario. I do not want to wrap the declared one. Both scenarios in my case leads to an action, but both should be able to be caught and handled differently.
@momo: so what you want to do is play exception lottery. The user of your API has no way to know which exceptions it actually may throw or wrap (and into which type). After all, no one can guaranty that you will be able to instantiate the declared exception type as it may have private constructors or be an abstract type. And as said, it doesn’t work with all scenarios anyway, even without lambdas.
@momo: none of the answers of that linked question have a solution for this case. It is simply impossible to get that type in this case. If you think you can live with the fact that your code is broken and works only for a specific case, well, then why do you bother with lambdas? It just means that there is another case for which your broken code doesn’t work…
No, it did not go from "one case to two". It went from "it works in this one case, and breaks in these 27" to "it works in this one case, but breaks in these 28".
You should learn what it means when the compiler warns you about an “unchecked cast”…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.