4

可能重复:
调用 TreeSet<Long>.contains(Long.valueOf(someLongValue)) 时出现 ClassCastException

问题请看截图:

奇怪的类型推断问题

似乎 Typed Set 中的一个条目cylinderIds突然变成了类型String- 但这是怎么发生的呢?

该 bean 是在 JSF 页面中使用的,但我一直认为 Java 中的类型系统应该防止这种情况发生……知道这里出了什么问题吗?

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

4

3 回答 3

2

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

当 JSF 将提交的输入值设置为 bean 属性时,它首先检索它们,String因为这是 的默认返回类型request.getParameter(),JSF 在幕后使用它来获取请求参数。只要没有指定转换器,JSF 就会在via 反射中设置那些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[]因此可能不是一个选项。为了解决它,您需要Converter在输入组件中显式指定 a 。您可以为此使用内置的 JSF LongConverter,它的转换器 ID 为javax.faces.Long

<h:selectManyListbox ... converter="javax.faces.Long">
于 2013-01-25T14:10:48.670 回答
1

第一个问题是您Iterator每次踏入while. 第二个问题是Iterator通用的,所以你最好做这样的事情:

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

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

第三个问题是我看不到你碰巧在哪里填充Set。如果您向我们展示代码,我可以为您提供更多帮助。

于 2013-01-25T12:38:25.903 回答
0

无需查看您的代码,我就可以回答某些事情正在改变您的集合类型:

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

oracle 文档的泛型文章更好地为您解释。

尝试此代码以查看问题何时发生:

Set<Long> s = Collections.checkedSet(new HashSet<Long>(), Long.class);
于 2013-01-25T12:44:39.130 回答