Java 弹簧罐';当使用lambda表达式而不是匿名内部类时,不能确定泛型类型
我正在使用Spring的Java 弹簧罐';当使用lambda表达式而不是匿名内部类时,不能确定泛型类型,java,spring,java-8,Java,Spring,Java 8,我正在使用Spring的ConversionService,添加一个简单的转换器,将ZonedDateTime(Java 8)转换为字符串: @Bean public ConversionServiceFactoryBean conversionServiceFactoryBean() { ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean
ConversionService
,添加一个简单的转换器,将ZonedDateTime
(Java 8)转换为字符串
:
@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;
}
@Bean
公共转换ServiceFactoryBean转换ServiceFactoryBean(){
转换服务工厂bean转换服务工厂bean=
新的转换ServiceFactoryBean();
转换器日期时间转换器=
新转换器(){
@凌驾
公共字符串转换(ZonedDateTime源){
返回source.format(DateTimeFormatter.ISO\u OFFSET\u DATE\u TIME);
}
};
conversionServiceFactoryBean.setConverters(
新的HashSet(Arrays.asList(dateTimeConverter));
返回转换ServiceFactoryBean;
}
这个很好用。但是我的IDE(IntelliJ)建议用lambda表达式替换匿名内部类:
Converter<ZonedDateTime, String> dateTimeConverter =
source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Converter日期时间转换器=
source->source.format(DateTimeFormatter.ISO\u OFFSET\u DATE\u TIME);
如果我这样做,那么它就不再工作了,我会得到一个关于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)
原因:java.lang.IllegalArgumentException:无法确定转换器之间转换的sourceType和targetType;声明这些泛型类型。
位于org.springframework.util.Assert.notNull(Assert.java:112)
位于org.springframework.core.convert.support.GenericConversionService.addConverter(GenericConversionService.java:100)
位于org.springframework.core.convert.support.ConversionServiceFactory.registerConverters(ConversionServiceFactory.java:50)
位于org.springframework.context.support.ConversionServiceFactoryBean.AfterPropertieSet(ConversionServiceFactoryBean.java:70)
位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627)
位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564)
表示lambda表达式的类
对象显然与匿名内部类的类
不同,因此Spring无法再确定泛型类型。Java8是如何使用lambda表达式实现这一点的?这是Spring中可以修复的bug,还是Java8只是没有提供必要的信息
我使用的是SpringVersion4.1.0.RELEASE和Java8update20。linked很好地解释了这个问题
基本上,在当前的JDK中,lambda的实际实现被编译到声明类中,JVM生成一个lambda类,其方法是擦除接口中声明的方法
所以
由生成的lambda类实例调用。在内部,函数接口方法简单地转换为上述方法的参数类型。
如果要重写的方法类型的擦除在其
删除U
函数类型后的签名
在评估或执行lambda主体时,方法主体会检查
每个参数值都是的子类或子接口的实例
删除函数类型中相应的参数类型
美国;如果不是,则抛出ClassCastException
VM本身生成一个重写方法,它是接口中声明的方法的原始等价物
关于这些类型的唯一信息是在上面的static
方法中。因为这个方法是声明类的一部分,所以Spring无法在给定由lambda表达式生成的实例的情况下检索它
但是,你可以这样做
interface ZonedDateTimeToStringConverter extends Converter<ZonedDateTime, String> {
}
这迫使lambda声明一个类似
private static java.lang.String com.example.Test.lambda$0(java.time.ZonedDateTime source) {
return source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
public String convert(ZonedDateTime zdt);
Spring将能够找到它并解析目标和源类型。检查:
Converter Converter=source->source.format(DateTimeFormatter.ISO\u OFFSET\u DATE\u TIME);
类[]typeArgs=TypeResolver.resolveRawArguments(Converter.Class,Converter.getClass());
断言typeArgs[0]==ZonedDateTime.class;
assert typeArgs[1]==String.class;
这种方法适用于Oracle/OpenJDK,因为它使用sun.reflect.ConstantPool
API
注意:我被要求将这项工作贡献给Spring,但我还没有空闲时间来做这项工作(简单地看,与Spring现有的泛型类型解析功能进行干净的集成似乎不是一件小事)。这看起来很难实现:谢谢,看起来这确实不是一件容易修复的事情。我想知道是否可以使用ASM来学习签名合成方法?谢谢。如果您有机会,请将此内容贡献给Spring,这将非常有用。@Jesper我想-我现在正忙于更高优先级的事情:)这是一张跟踪Spring框架的票。请注意,它只在Oracle JDK上工作这一事实是我们不愿意为此添加支持的原因之一:
Converter<ZonedDateTime, String> dateTimeConverter = (ZonedDateTimeToStringConverter)
source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
ZonedDateTimeToStringConverter dateTimeConverter = source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
public String convert(ZonedDateTime zdt);
Converter<ZonedDateTime, String> converter = source -> source.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Converter.class, converter.getClass());
assert typeArgs[0] == ZonedDateTime.class;
assert typeArgs[1] == String.class;