I am using <p:selectCheckboxMenu>
on a List<Long>
:
<p:selectCheckboxMenu value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
private List<Long> selectedItems;
private Map<String, Long> availableItems;
When submitting the form and looping over the selected items as below,
for (int i = 0; i < selectedItems.size(); i++) {
Long id = selectedItems.get(i);
// ...
}
Then I get a class cast exception:
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
The same problem occurs with <p:selectManyCheckbox>
, <p:selectManyMenu>
, <h:selectManyMenu>
, etc. All multiple-selection components basically. It works fine in <p:selectOneMenu>
and all other single-selection components on a single value Long
property.
How is this caused and how can I solve it?
Your problem is caused by the following facts:
String
s.Logical consequence is: EL doesn't see any generic type information. EL doesn't see a
List<Long>
, but aList
. So, when you don't explicitly specify a converter, EL will after obtaining the submitted value asString
set it unmodified in theList
by reflection means. When you attempt to cast it toLong
afterwards during runtime, you'll obviously face aClassCastException
.The solution is simple: explicitly specify a converter for
String
toLong
. You can use the JSF builtinLongConverter
for this which has the converter IDjavax.faces.Long
. Other builtin converters are listed here.Another solution without the need to explicitly specify the converter is to change
List<T>
type to aT[]
. This way the EL will see theLong
typed array and thus perform automatic conversion. But this possibly requires changes elsewhere in the model which may not be desirable.In case you're using a complex object (javabean, entity, POJO, etc) as select item value instead of a standard type like
Long
for which JSF has builtin converters, then the same rules also apply. You only need to create a customConverter
and explicitly specify it in input component'sconverter
attribute, or rely onforClass
if you can useT[]
. How to create such a converter is elaborated in Conversion Error setting value for 'null Converter'.