-
-
Save dgageot/bda57296107ca6a0e9df to your computer and use it in GitHub Desktop.
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"); | |
} | |
} |
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.
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 😃
Tested on Temurin JDK 11 and 17 right now - not working for both static and non-static lambdas.
very good!