Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 弹簧罐';当使用lambda表达式而不是匿名内部类时,不能确定泛型类型_Java_Spring_Java 8 - Fatal编程技术网

Java 弹簧罐';当使用lambda表达式而不是匿名内部类时,不能确定泛型类型

Java 弹簧罐';当使用lambda表达式而不是匿名内部类时,不能确定泛型类型,java,spring,java-8,Java,Spring,Java 8,我正在使用Spring的ConversionService,添加一个简单的转换器,将ZonedDateTime(Java 8)转换为字符串: @Bean public ConversionServiceFactoryBean conversionServiceFactoryBean() { ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean

我正在使用Spring的
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;