jhalterman / typetools

Tools for working with generic types
Apache License 2.0
617 stars 94 forks source link

Lambda resolver limitation #40

Closed chhsiao90 closed 7 years ago

chhsiao90 commented 7 years ago

I had searched for issues but didn't find any similar issue. And I had tried openjdk64-1.8.0.91 and oracle64-1.8.0.101, all seems to have same problem.

For following case, at test case shouldResolveTypesForLambda that we cannot get the generic type of Integer when defined a lambda of Function<Integer, String>. Is that a limitation? Thanks!

import static org.testng.Assert.assertEquals;

import net.jodah.typetools.AbstractTypeResolverTest;
import net.jodah.typetools.TypeResolver;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;

@Test
public class LambdaOfContextTest extends AbstractTypeResolverTest {
    @Factory(dataProvider = "cacheDataProvider")
    public LambdaOfContextTest(boolean cacheEnabled) {
        super(cacheEnabled);
    }

    interface Context<S, D> {
        S getSource();

        D getDestination();
    }

    interface Function<S, D> {
        D apply(Context<S, D> context);
    }

    public void shouldResolveTypesForInnerClass() {
        Function<Integer, String> innerFunction = new Function<Integer, String>() {
            @Override
            public String apply(Context<Integer, String> context) {
                return context.getSource().toString();
            }
        };

        Class<?>[] innerFuncArgs = TypeResolver.resolveRawArguments(Function.class, innerFunction.getClass());
        assertEquals(innerFuncArgs[0], Integer.class);
        assertEquals(innerFuncArgs[1], String.class);
    }

    public void shouldResolveTypesForLambda() {
        Function<Integer, String> lambdaFunction = ctx -> ctx.getSource().toString();

        Class<?>[] lambdaFuncArgs = TypeResolver.resolveRawArguments(Function.class, lambdaFunction.getClass());
        assertEquals(lambdaFuncArgs[0], Integer.class); // Get Unknown
        assertEquals(lambdaFuncArgs[1], String.class);
    }
}
jhalterman commented 7 years ago

I believe this is a limitation of the technique we're using and sounds similar to issue #14. Check out my comment here since I think it's a similar situation.

Basically, in the statement:

Function<Integer, String> lambdaFunction = ctx -> ctx.getSource().toString();

Type information on the lefthand side is erased at runtime, so Function<Integer, String> becomes Function. So we rely on the righthand side in order to resolve the type arguments for the Function. In this case, the righthand side is an instance of Context<S, D>, and there is not enough information on the righthand side to know what the value of S is...

chhsiao90 commented 7 years ago

Thanks for the information. I had even tried SerializedLambda and it seems that it had same limitation too.

Knowing the limitation is good enough to me, thanks!