请看屏幕截图中的问题:
在类型化的集合中,cylinderIds
中的条目似乎突然变成了String
类型--但是这是如何发生的呢?
bean是在JSF页面中使用的,但是我一直认为Java中的类型系统应该防止这种情况.知道这里出了什么问题吗?
在Windows7上使用1.7.0_06 64位,应用程序在同一个JBoss版本的JBoss 7.1.0中运行。
发布于 2013-01-25 06:10:48
这并不完全是Java的错。泛型类型信息只是在运行时丢失。Java/JSF/EL不是在编译时运行,而是在运行时运行。它在运行时看到的只是一个Set
,而不是一个Set<Long>
。
当JSF将提交的输入值设置为bean属性时,它首先以String
的形式检索输入值,因为这是request.getParameter()
的默认返回类型,JSF在幕后使用该类型获取请求参数。只要没有指定转换器,JSF就会通过反射设置未在Set
中转换的提交的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
。您可以为此使用JSF内置的LongConverter
,它的转换器ID为javax.faces.Long
<h:selectManyListbox ... converter="javax.faces.Long">
发布于 2013-01-25 04:38:25
第一个问题是,每次进入Iterator
时都会请求一个新的while
。第二个问题是Iterator
是通用的,所以最好这样做:
Iterator<Long> cylinderIter = cylinderIds.iterator();
while(cylinderIter.hasNext()) {
cylinderIter.next() // ...
// do something
}
第三个问题是,我看不出您在哪里填充该Set
。如果你给我们看密码我可以帮你更多。
发布于 2013-01-25 04:44:39
如果没有看到您的代码,我可以回答您的集合的类型正在发生变化:
泛型是通过类型擦除实现的:泛型类型信息仅在编译时出现,然后由编译器删除。这种方法的主要优点是它提供了使用非参数化类型(在技术上称为原始类型)的泛型代码和遗留代码之间的完全互操作性。的主要缺点是在运行时无法获得参数类型信息,并且当与行为不正常的遗留代码进行互操作时,自动生成的转换可能会失败。然而,有一种方法可以保证通用集合的运行时类型安全,即使与行为不佳的遗留代码进行互操作。
oracle文档的仿制药文章更好地解释了您。
试试下面的代码,看看问题何时发生:
Set<Long> s = Collections.checkedSet(new HashSet<Long>(), Long.class);
https://stackoverflow.com/questions/14521882
复制