Skip to content

Instantly share code, notes, and snippets.

@dgageot
Created August 19, 2014 10:03
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgageot/bda57296107ca6a0e9df to your computer and use it in GitHub Desktop.
Save dgageot/bda57296107ca6a0e9df to your computer and use it in GitHub Desktop.
Find Lambda parameter types
package test;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.function.Function;
import static java.util.stream.Stream.of;
public class FindParametersTypes {
public static void main(String[] args) {
Function<String, String> inner = new Function<String, String>() {
@Override
public String apply(String s) {
return s;
}
};
Function<String, String> lambda = s -> s;
of(inner, lambda).forEach(f -> System.out.println(Arrays.toString(firstParameter(f))));
of(inner, lambda).forEach(f -> System.out.println(Arrays.toString(firstParameterSmart(f))));
}
private static Class<?>[] firstParameter(Function<String, String> function) {
return function.getClass().getMethods()[0].getParameterTypes();
}
// Lambda class name: test.Toto$$Lambda$1/1199823423
// Implementation synthetic method: lambda$main$0
//
private static Class<?>[] firstParameterSmart(Function<String, String> function) {
String functionClassName = function.getClass().getName();
int lambdaMarkerIndex = functionClassName.indexOf("$$Lambda$");
if (lambdaMarkerIndex == -1) { // Not a lambda
return firstParameter(function);
}
String declaringClassName = functionClassName.substring(0, lambdaMarkerIndex);
int lambdaIndex = Integer.parseInt(functionClassName.substring(lambdaMarkerIndex + 9, functionClassName.lastIndexOf('/')));
Class<?> declaringClass;
try {
declaringClass = Class.forName(declaringClassName);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to find lambda's parent class " + declaringClassName);
}
for (Method method : declaringClass.getDeclaredMethods()) {
if (method.isSynthetic()
&& method.getName().startsWith("lambda$")
&& method.getName().endsWith("$" + (lambdaIndex - 1))
&& Modifier.isStatic(method.getModifiers())) {
return method.getParameterTypes();
}
}
throw new IllegalStateException("Unable to find lambda's implementation method");
}
}
@orangeboyye
Copy link

very good!

@ruiruige
Copy link

ruiruige commented Nov 9, 2018

seems good. There are many answers, which can solve the problem "how to get parameterized type from a function/predicate",
but when the IDE format/refactor them into a lambda, those answers will not work any more. Because they rely on the method "getGenericInterfaces"

When it is function or predicate or something else, using "getGenericInterfaces" can get a ParameterizedType object and can use getActualTypeArguments of it, but when it comes to lambda, the "getGenericInterfaces" method returns a class rather then a ParameterizedType.

Your gist helps a lot and it can solve the problem.

@kamatama41
Copy link

kamatama41 commented Mar 21, 2019

Thanks for the great example! But I found an exceptional case.
If the lambda for java.util.Function is declared in a Lambda expression, the index between the Lambda object and the method in the root class become different. (My environment is Oracle JDK 1.8.0_171 on MacOS)

public class FindParametersTypes {
    public static void main(String[] args) {
        Function<String, String> inner = new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s;
            }
        };

        Function<String, String> lambda = s -> s;

        Runnable task = () -> {
            Function<String, String> lambdaInLambda = s -> s;
            of(inner, lambda, lambdaInLambda).forEach(f -> System.out.println(Arrays.toString(firstParameterSmart(f))));
        };
        task.run();
    }

In the example above, the name of class of lambdaInLambda is like test.FindParametersTypes$$Lambda$3/1349393271 but the corresponding method in the root class is lambda$null$1 and lambda$null$2 is actually the lambda for forEach so that the firstParameterSmart would cause a misditection.

[class java.lang.String]
[class java.lang.String]
[interface java.util.function.Function]

Although I have not found the solution of the case, just a heads up to you 😃

@api-from-the-ion
Copy link

Tested on Temurin JDK 11 and 17 right now - not working for both static and non-static lambdas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment