UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T
Solution 1:
Your problem is caused by the following facts:
- Java generics are compiletime syntactic sugar and completely absent during runtime.
- EL expressions runs during runtime and not during compiletime.
- HTTP request parameters are obtained as
String
s.
Logical consequence is: EL doesn't see any generic type information. EL doesn't see a List<Long>
, but a List
. So, when you don't explicitly specify a converter, EL will after obtaining the submitted value as String
set it unmodified in the List
by reflection means. When you attempt to cast it to Long
afterwards during runtime, you'll obviously face a ClassCastException
.
The solution is simple: explicitly specify a converter for String
to Long
. You can use the JSF builtin LongConverter
for this which has the converter ID javax.faces.Long
. Other builtin converters are listed here.
<p:selectCheckboxMenu ... converter="javax.faces.Long">
Another solution without the need to explicitly specify the converter is to change List<T>
type to a T[]
. This way the EL will see the Long
typed array and thus perform automatic conversion. But this possibly requires changes elsewhere in the model which may not be desirable.
private Long[] selectedItems;
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 custom Converter
and explicitly specify it in input component's converter
attribute, or rely on forClass
if you can use T[]
. How to create such a converter is elaborated in Conversion Error setting value for 'null Converter'.