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();
        }

Reply via email to