Java推断错误类型的类型化HashSet

时间:2013-01-25 12:31:41

标签: java jsf generics

  

可能重复:
  ClassCastException when calling TreeSet<Long>.contains( Long.valueOf( someLongValue ) )

请查看问题的屏幕截图:

Strange type inference problem

类似集cylinderIds中的条目似乎突然出现String类型 - 但这是怎么发生的?

bean在JSF页面中使用,但我一直认为Java中的类型系统应该阻止这个......任何想法在这里出了什么问题?

在Windows 7上使用1.7.0_06 64位,该应用程序在同一Java版本的JBoss 7.1.0中运行。

3 个答案:

答案 0 :(得分:2)

这不完全是Java的错。通用类型信息在运行时丢失。 Java / JSF / EL在编译期间不运行,但在运行时期间运行。它在运行时期间看到的只是Set,而不是Set<Long>

当JSF将提交的输入值设置为bean属性时,它首先将它们检索为String,因为它是request.getParameter()的默认返回类型,JSF正在使用它来获取请求参数。只要没有指定转换器,JSF就会通过反射在String中设置未转换的提交的Set值。这基本上就是“幕后”发生的事情:

package com.stackoverflow.q14521882;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class Test {

    private Set<Long> set = new HashSet<Long>();

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        Field field = test.getClass().getDeclaredField("set");
        Object object = field.get(test);
        if (object instanceof Collection) {
            ((Collection) object).add("string");
        }
        System.out.println(test.set.iterator().next().getClass()); // CCE!
    }

}

当您使用Long[]代替Set<Long>时,它会有效,但鉴于它首先是Set,您只想保留唯一值Long[]因此,1}}可能不是一种选择。为了解决这个问题,您需要在输入组件中明确指定Converter。您可以使用JSF内置LongConverter,其转换器ID为javax.faces.Long

<h:selectManyListbox ... converter="javax.faces.Long">

答案 1 :(得分:1)

第一个问题是,每次进入Iterator时,您都会要求新的while。第二个问题是Iterator是通用的,所以你最好做这样的事情:

Iterator<Long> cylinderIter = cylinderIds.iterator();

while(cylinderIter.hasNext()) {
    cylinderIter.next() // ...
    // do something
}

第三个问题是,我无法看到您填充Set的位置。如果您向我们展示代码,我可以为您提供更多帮助。

答案 2 :(得分:0)

如果没有看到您的代码,我可以回答有些内容正在改变您的代码类型:

  

泛型是通过类型擦除实现的:泛型类型信息是   仅在编译时出现,之后它被删除   编译器。这种方法的主要优点是它提供了   通用代码与使用的遗留代码之间的完全互操作性   非参数化类型(技术上称为原始类型)。   主要缺点是参数类型信息在运行时不可用,并且自动生成的强制转换可能会失败   与不良遗留代码进行互操作时。但是,   一种实现通用的运行时类型安全性的方法   即使与不良遗留代码进行互操作,也可以收集。

oracle文档的generics文章更好地解释了你。

尝试使用此代码查看问题发生的时间:

Set<Long> s = Collections.checkedSet(new HashSet<Long>(), Long.class);