@PublicEvolving
    public static <IN, OUT> TypeInformation<OUT> getMapReturnTypes(
            MapFunction<IN, OUT> mapInterface, TypeInformation<IN> inType) {
        return getMapReturnTypes(mapInterface, inType, null, false);
    }
@Test
    void testAbstractAndInterfaceTypes() {

        // interface
        RichMapFunction<String, ?> function =
                new RichMapFunction<String, Testable>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Testable map(String value) throws Exception {
                        return null;
                    }
                };

        TypeInformation<?> ti =
                TypeExtractor.getMapReturnTypes(function, BasicTypeInfo.STRING_TYPE_INFO);
        assertThat(ti).isInstanceOf(GenericTypeInfo.class);

        // abstract class with out class member
        RichMapFunction<String, ?> function2 =
                new RichMapFunction<String, AbstractClassWithoutMember>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public AbstractClassWithoutMember map(String value) throws Exception {
                        return null;
                    }
                };

        ti = TypeExtractor.getMapReturnTypes(function2, BasicTypeInfo.STRING_TYPE_INFO);
        assertThat(ti).isInstanceOf(GenericTypeInfo.class);

        // abstract class with class member
        RichMapFunction<String, ?> function3 =
                new RichMapFunction<String, AbstractClassWithMember>() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public AbstractClassWithMember map(String value) throws Exception {
                        return null;
                    }
                };

        ti = TypeExtractor.getMapReturnTypes(function3, BasicTypeInfo.STRING_TYPE_INFO);
        assertThat(ti).isInstanceOf(PojoTypeInfo.class);
    }