diff --git a/src/main/java/net/jodah/typetools/TypeResolver.java b/src/main/java/net/jodah/typetools/TypeResolver.java index 6671eb1..666bec5 100644 --- a/src/main/java/net/jodah/typetools/TypeResolver.java +++ b/src/main/java/net/jodah/typetools/TypeResolver.java @@ -319,14 +319,15 @@ private static void populateLambdaArgs(Class functionalInterface, final Class try { constantPool = (ConstantPool) GET_CONSTANT_POOL.invoke(lambdaType); } catch (Exception e) { - break; + return; } String[] methodRefInfo = getMethodRefInfo(constantPool); if (methodRefInfo == null) { - break; + return; } + // Populate return type argument if (returnTypeVar instanceof TypeVariable) { Class returnType = TypeDescriptor.getReturnType(methodRefInfo[2]).getType(lambdaType.getClassLoader()); if (!returnType.equals(Void.class)) @@ -335,7 +336,7 @@ private static void populateLambdaArgs(Class functionalInterface, final Class TypeDescriptor[] arguments = TypeDescriptor.getArgumentTypes(methodRefInfo[2]); - // Handle arbitrary object instance method references + // Populate object type from arbitrary object method reference int paramOffset = 0; if (paramTypeVars.length > 0 && paramTypeVars[0] instanceof TypeVariable && paramTypeVars.length == arguments.length + 1) { @@ -344,19 +345,21 @@ private static void populateLambdaArgs(Class functionalInterface, final Class paramOffset = 1; } - // Handle local final variables from context that are passed as arguments. + // Handle additional arguments that are captured from the lambda's enclosing scope int argOffset = 0; if (paramTypeVars.length < arguments.length) { argOffset = arguments.length - paramTypeVars.length; } + // Populate type parameter arguments for (int i = 0; i + argOffset < arguments.length; i++) { if (paramTypeVars[i] instanceof TypeVariable) { map.put((TypeVariable) paramTypeVars[i + paramOffset], arguments[i + argOffset].getType(lambdaType.getClassLoader())); } } - break; + + return; } } } @@ -444,8 +447,7 @@ public static Type resolveBound(TypeVariable typeVariable) { /** * Resolves method ref info. * - * @return the method ref info for the {@code constantPool}, or null if no appropriate method ref - * could be found. + * @return the method ref info for the {@code constantPool}, or null if no appropriate method ref could be found. */ private static String[] getMethodRefInfo(ConstantPool constantPool) { String[] returnValue = null; diff --git a/src/test/java/net/jodah/typetools/functional/LambdaTest.java b/src/test/java/net/jodah/typetools/functional/LambdaTest.java index 3f85613..0e8f08f 100644 --- a/src/test/java/net/jodah/typetools/functional/LambdaTest.java +++ b/src/test/java/net/jodah/typetools/functional/LambdaTest.java @@ -33,6 +33,10 @@ public LambdaTest(boolean cacheEnabled) { super(cacheEnabled); } + interface TriPredicate { + boolean test(A a, B b, C c); + } + interface FnSubclass extends Function { } @@ -65,6 +69,10 @@ boolean evaluate(String s) { return false; } + boolean evaluate2(String s, int i) { + return false; + } + static boolean eval(String s) { return false; } @@ -108,8 +116,10 @@ public void shouldResolveArguments() { assertEquals(TypeResolver.resolveRawArgument(Consumer.class, consumer.getClass()), String.class); } - public void shouldResolveArgumentsCorrectly() { - + /** + * Asserts that arguments captured from the enclosing scope can be resolved. + */ + public void shouldResolveCapturedArguments() { final AtomicLong a = new AtomicLong(0); Function func = (s) -> { a.incrementAndGet(); @@ -117,24 +127,40 @@ public void shouldResolveArgumentsCorrectly() { }; assertEquals(new Class[] {String.class, Long.class}, TypeResolver.resolveRawArguments(Function.class, func.getClass())); - } /** - * Asserts that arguments can be resolved from method references for simple functional interfaces. + * Asserts that arguments can be resolved from instance method references for simple functional interfaces. */ - public void shouldResolveArgumentsFromMethodRefs() { + public void shouldResolveArgumentsFromInstanceMethodRefs() { Baz baz = new Baz(); Predicate p1 = baz::evaluate; - Predicate p2 = Baz::eval; - BiPredicate p3 = Baz::evaluate; + + assertEquals(TypeResolver.resolveRawArgument(Predicate.class, p1.getClass()), String.class); + } + + /** + * Asserts that arguments can be resolved from static method references for simple functional interfaces. + */ + public void shouldResolveArgumentsFromStaticMethodRefs() { Comparator c = String::compareToIgnoreCase; + assertEquals(TypeResolver.resolveRawArgument(Comparator.class, c.getClass()), String.class); + } + + /** + * Asserts that arguments can be resolved from arbitrary object method references for simple functional interfaces. + */ + public void shouldResolveArgumentsFromArbitraryObjectMethodRefs() { + Predicate p1 = Baz::eval; + BiPredicate p2 = Baz::evaluate; + TriPredicate p3 = Baz::evaluate2; + assertEquals(TypeResolver.resolveRawArgument(Predicate.class, p1.getClass()), String.class); - assertEquals(TypeResolver.resolveRawArgument(Predicate.class, p2.getClass()), String.class); - assertEquals(TypeResolver.resolveRawArguments(BiPredicate.class, p3.getClass()), + assertEquals(TypeResolver.resolveRawArguments(BiPredicate.class, p2.getClass()), new Class[] { Baz.class, String.class }); - assertEquals(TypeResolver.resolveRawArgument(Comparator.class, c.getClass()), String.class); + assertEquals(TypeResolver.resolveRawArguments(TriPredicate.class, p3.getClass()), + new Class[] { Baz.class, String.class, Integer.class }); } /**