>我在List<Long>
上使用<p:selectCheckboxMenu>
:
<p:selectCheckboxMenu value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
private List<Long> selectedItems;
private Map<String, Long> availableItems;
提交表单并循环查看所选项目时,如下所示,
for (int i = 0; i < selectedItems.size(); i++) {
Long id = selectedItems.get(i);
// ...
}
然后我得到一个类转换异常:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at com.example.Bean.submit(Bean.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
... 27 more
同样的问题也发生在<p:selectManyCheckbox>
、<p:selectManyMenu>
、<h:selectManyMenu>
等。基本上都是多选组件。它在单个值Long
属性上的<p:selectOneMenu>
和所有其他单选组件中工作正常。
这是如何造成的,我该如何解决?
您的问题是由以下事实引起的:
- Java 泛型是编译时语法糖,在运行时完全不存在。
- EL 表达式在运行时运行,而不是在编译时运行。
- HTTP 请求参数以
String
s 的形式获取。
逻辑结果是:EL 看不到任何泛型类型信息。EL看到的不是List<Long>
,而是List
。因此,当您没有明确指定转换器时,EL 将在获取提交的值后String
通过反射方式在List
中将其设置为不修改。当您尝试在运行时将其转换为Long
时,您显然会面临ClassCastException
。
解决方案很简单:显式指定一个转换器供String
Long
。为此,您可以使用 JSF 内置LongConverter
,其转换器 ID javax.faces.Long
。此处列出了其他内置转换器。
<p:selectCheckboxMenu ... converter="javax.faces.Long">
无需显式指定转换器的另一种解决方案是将List<T>
类型更改为 T[]
.这样,EL 将看到Long
类型化数组,从而执行自动转换。但这可能需要在模型中的其他地方进行更改,这可能是不希望的。
private Long[] selectedItems;
如果您使用复杂对象(javabean、实体、POJO 等)作为选择项值,而不是像 JSF 内置转换器Long
这样的标准类型,则相同的规则也适用。您只需要创建自定义Converter
并在输入组件的 converter
属性中显式指定它,或者如果可以使用 T[]
,则依赖forClass
。如何创建这样的转换器在"空转换器"的转换错误设置值中详细说明。