Repository: flink Updated Branches: refs/heads/master 8d25c6414 -> ff777084b
[FLINK-3138] [types] Method References are not supported as lambda expressions This closes #2329. Project: http://git-wip-us.apache.org/repos/asf/flink/repo Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/ff777084 Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/ff777084 Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/ff777084 Branch: refs/heads/master Commit: ff777084ba2e1f1070ce5ecbc5afc122756ba851 Parents: 8d25c64 Author: twalthr <twal...@apache.org> Authored: Wed Aug 3 14:18:40 2016 +0200 Committer: twalthr <twal...@apache.org> Committed: Mon Aug 8 11:11:45 2016 +0200 ---------------------------------------------------------------------- .../flink/api/java/typeutils/TypeExtractor.java | 30 +++++++++-- .../java/type/lambdas/LambdaExtractionTest.java | 56 +++++++++++++++++++- .../javaApiOperators/lambdas/MapITCase.java | 12 ++--- 3 files changed, 85 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flink/blob/ff777084/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java ---------------------------------------------------------------------- diff --git a/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java b/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java index 9d30743..aaa8e0d 100644 --- a/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java +++ b/flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java @@ -345,12 +345,22 @@ public class TypeExtractor { if (m != null) { // check for lambda type erasure validateLambdaGenericParameters(m); - + // parameters must be accessed from behind, since JVM can add additional parameters e.g. when using local variables inside lambda function final int paramLen = m.getGenericParameterTypes().length - 1; - final Type input = (outputTypeArgumentIndex >= 0) ? m.getGenericParameterTypes()[paramLen - 1] : m.getGenericParameterTypes()[paramLen]; - validateInputType((inputTypeArgumentIndex >= 0) ? extractTypeArgument(input, inputTypeArgumentIndex) : input, inType); - if(function instanceof ResultTypeQueryable) { + + // method references "this" implicitly + if (paramLen < 0) { + // methods declaring class can also be a super class of the input type + // we only validate if the method exists in input type + validateInputContainsMethod(m, inType); + } + else { + final Type input = (outputTypeArgumentIndex >= 0) ? m.getGenericParameterTypes()[paramLen - 1] : m.getGenericParameterTypes()[paramLen]; + validateInputType((inputTypeArgumentIndex >= 0) ? extractTypeArgument(input, inputTypeArgumentIndex) : input, inType); + } + + if (function instanceof ResultTypeQueryable) { return ((ResultTypeQueryable<OUT>) function).getProducedType(); } return new TypeExtractor().privateCreateTypeInfo( @@ -1234,7 +1244,17 @@ public class TypeExtractor { } } } - + + private static void validateInputContainsMethod(Method m, TypeInformation<?> typeInfo) { + List<Method> methods = getAllDeclaredMethods(typeInfo.getTypeClass()); + for (Method method : methods) { + if (method.equals(m)) { + return; + } + } + throw new InvalidTypesException("Type contains no method '" + m.getName() + "'."); + } + // -------------------------------------------------------------------------------------------- // Utility methods // -------------------------------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flink/blob/ff777084/flink-java8/src/test/java/org/apache/flink/api/java/type/lambdas/LambdaExtractionTest.java ---------------------------------------------------------------------- diff --git a/flink-java8/src/test/java/org/apache/flink/api/java/type/lambdas/LambdaExtractionTest.java b/flink-java8/src/test/java/org/apache/flink/api/java/type/lambdas/LambdaExtractionTest.java index b4fffed..64b7ae7 100644 --- a/flink-java8/src/test/java/org/apache/flink/api/java/type/lambdas/LambdaExtractionTest.java +++ b/flink-java8/src/test/java/org/apache/flink/api/java/type/lambdas/LambdaExtractionTest.java @@ -70,13 +70,17 @@ public class LambdaExtractionTest { } }; - MapFunction<String, Integer> lambda = Integer::parseInt; + MapFunction<String, Integer> staticLambda = Integer::parseInt; + MapFunction<Integer, String> instanceLambda = Object::toString; + MapFunction<String, Integer> constructorLambda = Integer::new; assertNull(FunctionUtils.checkAndExtractLambdaMethod(anonymousFromInterface)); assertNull(FunctionUtils.checkAndExtractLambdaMethod(anonymousFromClass)); assertNull(FunctionUtils.checkAndExtractLambdaMethod(fromProperClass)); assertNull(FunctionUtils.checkAndExtractLambdaMethod(fromDerived)); - assertNotNull(FunctionUtils.checkAndExtractLambdaMethod(lambda)); + assertNotNull(FunctionUtils.checkAndExtractLambdaMethod(staticLambda)); + assertNotNull(FunctionUtils.checkAndExtractLambdaMethod(instanceLambda)); + assertNotNull(FunctionUtils.checkAndExtractLambdaMethod(constructorLambda)); assertNotNull(FunctionUtils.checkAndExtractLambdaMethod(STATIC_LAMBDA)); } catch (Exception e) { @@ -248,4 +252,52 @@ public class LambdaExtractionTest { Assert.assertTrue(ti instanceof MissingTypeInfo); } + public static class MyType { + private int key; + + public int getKey() { + return key; + } + + public void setKey(int key) { + this.key = key; + } + + protected int getKey2() { + return 0; + } + } + + @Test + public void testInstanceMethodRefSameType() { + MapFunction<MyType, Integer> f = MyType::getKey; + TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(f, TypeExtractor.createTypeInfo(MyType.class)); + Assert.assertEquals(ti, BasicTypeInfo.INT_TYPE_INFO); + } + + @Test + public void testInstanceMethodRefSuperType() { + MapFunction<Integer, String> f = Object::toString; + TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(f, BasicTypeInfo.INT_TYPE_INFO); + Assert.assertEquals(ti, BasicTypeInfo.STRING_TYPE_INFO); + } + + public static class MySubtype extends MyType { + public boolean test; + } + + @Test + public void testInstanceMethodRefSuperTypeProtected() { + MapFunction<MySubtype, Integer> f = MyType::getKey2; + TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(f, TypeExtractor.createTypeInfo(MySubtype.class)); + Assert.assertEquals(ti, BasicTypeInfo.INT_TYPE_INFO); + } + + @Test + public void testConstructorMethodRef() { + MapFunction<String, Integer> f = Integer::new; + TypeInformation<?> ti = TypeExtractor.getMapReturnTypes(f, BasicTypeInfo.STRING_TYPE_INFO); + Assert.assertEquals(ti, BasicTypeInfo.INT_TYPE_INFO); + } + } http://git-wip-us.apache.org/repos/asf/flink/blob/ff777084/flink-java8/src/test/java/org/apache/flink/test/javaApiOperators/lambdas/MapITCase.java ---------------------------------------------------------------------- diff --git a/flink-java8/src/test/java/org/apache/flink/test/javaApiOperators/lambdas/MapITCase.java b/flink-java8/src/test/java/org/apache/flink/test/javaApiOperators/lambdas/MapITCase.java index d4cf585..cda1f1c 100644 --- a/flink-java8/src/test/java/org/apache/flink/test/javaApiOperators/lambdas/MapITCase.java +++ b/flink-java8/src/test/java/org/apache/flink/test/javaApiOperators/lambdas/MapITCase.java @@ -24,10 +24,10 @@ import org.apache.flink.test.util.JavaProgramTestBase; public class MapITCase extends JavaProgramTestBase { - private static final String EXPECTED_RESULT = "bb\n" + - "bb\n" + - "bc\n" + - "bd\n"; + private static final String EXPECTED_RESULT = "22\n" + + "22\n" + + "23\n" + + "24\n"; private String resultPath; @@ -40,8 +40,8 @@ public class MapITCase extends JavaProgramTestBase { protected void testProgram() throws Exception { final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); - DataSet<String> stringDs = env.fromElements("aa", "ab", "ac", "ad"); - DataSet<String> mappedDs = stringDs.map (s -> s.replace("a", "b")); + DataSet<Integer> stringDs = env.fromElements(11, 12, 13, 14); + DataSet<String> mappedDs = stringDs.map(Object::toString).map (s -> s.replace("1", "2")); mappedDs.writeAsText(resultPath); env.execute(); }