当使用lambda表达式而不是匿名内部类时,Spring无法确定泛型类型

Jes*_*per 8 java spring java-8

我正在玩Spring ConversionService,添加一个简单的转换器将ZonedDateTime(Java 8)转换为String:

@Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean() {
    ConversionServiceFactoryBean conversionServiceFactoryBean =
        new ConversionServiceFactoryBean();

    Converter<ZonedDateTime, String> dateTimeConverter =
        new Converter<ZonedDateTime, String>() {
            @Override
            public String convert(ZonedDateTime source) {
                return source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
            }
        };

    conversionServiceFactoryBean.setConverters(
        new HashSet<>(Arrays.asList(dateTimeConverter)));
    return conversionServiceFactoryBean;
}
Run Code Online (Sandbox Code Playgroud)

这很好用.但我的IDE(IntelliJ)建议用lambda表达式替换匿名内部类:

Converter<ZonedDateTime, String> dateTimeConverter =
    source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Run Code Online (Sandbox Code Playgroud)

如果我这样做,那么它不再起作用,我得到一个关于Spring无法确定泛型类型的错误:

Caused by: java.lang.IllegalArgumentException: Unable to the determine sourceType <S> and targetType <T> which your Converter<S, T> converts between; declare these generic types.
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.core.convert.support.GenericConversionService.addConverter(GenericConversionService.java:100)
    at org.springframework.core.convert.support.ConversionServiceFactory.registerConverters(ConversionServiceFactory.java:50)
    at org.springframework.context.support.ConversionServiceFactoryBean.afterPropertiesSet(ConversionServiceFactoryBean.java:70)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564)
Run Code Online (Sandbox Code Playgroud)

Class表示lambda表达式的对象明显不同于ClassSpring无法确定泛型类型的匿名内部类.Java 8如何使用lambda表达式完成这项工作?这是Spring中的一个可以修复的错误,还是Java 8没有提供必要的信息?

我正在使用Spring版本4.1.0.RELEASE和Java 8更新20.

Sot*_*lis 7

Alan Stokes在评论中链接的这篇文章很好地解释了这个问题.

基本上,在当前的JDK中,lambda的实际实现被编译到声明类中,JVM生成一个Lambda类,其方法是在接口中声明的方法的擦除.

所以

Converter<ZonedDateTime, String> dateTimeConverter =
    source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Run Code Online (Sandbox Code Playgroud)

产生一种合成方法

private static java.lang.String com.example.Test.lambda$0(java.time.ZonedDateTime source)  {
    return source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
Run Code Online (Sandbox Code Playgroud)

由生成的lambda类实例调用.在内部,功能接口方法简单地转换为上述方法的参数类型.该JLS状态

如果被覆盖的方法类型的擦除在其签名上与函数类型的擦除不同U,那么在评估或执行lambda主体之前,方法的主体检查每个参数值是否为子类或子接口的实例. U的函数类型中相应参数类型的擦除; 如果没有,ClassCastException则抛出一个.

VM本身生成一个重写方法,它是接口中声明的方法的原始等效方法.

关于类型的唯一信息是static上面的方法.由于此方法是声明类的一部分,因此在给定从lambda表达式生成的实例的情况下,Spring无法检索它.

但是,你可以做到

interface ZonedDateTimeToStringConverter extends Converter<ZonedDateTime, String> {
}
Run Code Online (Sandbox Code Playgroud)

Converter<ZonedDateTime, String> dateTimeConverter = (ZonedDateTimeToStringConverter)
    source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Run Code Online (Sandbox Code Playgroud)

要么

ZonedDateTimeToStringConverter dateTimeConverter =  source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Run Code Online (Sandbox Code Playgroud)

这迫使lambda声明一个类似的方法

public String convert(ZonedDateTime zdt);
Run Code Online (Sandbox Code Playgroud)

和Spring将能够找到它并解析目标和源类型.