List <T>上的UISelectMany导致java.lang.ClassCastException:java.lang.String无法强制转换为T

Sha*_*oor 4 jsf classcastexception primefaces jsf-2 selectmanycheckbox

我使用<p:selectCheckboxMenu>List<Long>:

<p:selectCheckboxMenu value="#{bean.selectedItems}">
    <f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
Run Code Online (Sandbox Code Playgroud)
private List<Long> selectedItems;
private Map<String, Long> availableItems;
Run Code Online (Sandbox Code Playgroud)

提交表单并循环显示所选项目时,如下所示,

for (int i = 0; i < selectedItems.size(); i++) {
    Long id = selectedItems.get(i);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

然后我得到一个类强制转换异常:

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
Run Code Online (Sandbox Code Playgroud)

与出现同样的问题<p:selectManyCheckbox>,<p:selectManyMenu>,<h:selectManyMenu>等所有多选组件基本.它在<p:selectOneMenu>单个值Long属性上和所有其他单选组件中都可以正常工作.

这是怎么造成的,我该如何解决?

Bal*_*usC 8

您的问题是由以下事实引起的:

  1. Java泛型是编译时语法糖,在运行时完全不存在.
  2. EL表达式在运行时运行,而不是在编译期间运行.
  3. HTTP请求参数以s 获得String.

逻辑后果是:EL没有看到任何泛型类型信息.EL没有看到List<Long>,但是a List.因此,当您没有明确指定转换器时,EL将在获取提交的值之后String将其设置为未List通过反射方式修改.当你Long在运行时尝试将它投射到之后,你显然会遇到一个问题ClassCastException.

解决方法很简单:明确指定一个转换器StringLong.您可以使用LongConverter具有转换器ID 的JSF内置javax.faces.Long.此处列出其他内置转换器.

<p:selectCheckboxMenu ... converter="javax.faces.Long">
Run Code Online (Sandbox Code Playgroud)

无需明确指定转换器的另一种解决方案是将List<T>类型更改为a T[].这样EL将看到Long类型化数组,从而执行自动转换.但这可能需要在模型中的其他地方进行更改,这可能是不可取的.

private Long[] selectedItems;
Run Code Online (Sandbox Code Playgroud)

如果您使用复杂对象(javabean,实体,POJO等)作为选择项值而不是LongJSF具有内置转换器的标准类型,则同样的规则也适用.您只需要创建一个自定义Converter并在输入组件的converter属性中明确指定它,或者依赖于forClass您是否可以使用T[].如何创建这样的转换器在'null Converter'的转换错误设置值中详细说明.