此问题与枚举无关。对于 JSF 具有内置转换器的其他 List 类型,您会遇到同样的问题,例如List<Integer>,List<Double>,等等。
问题在于 EL 在运行时运行,而泛型类型信息在运行时丢失。所以本质上,JSF/EL 对List 的参数化类型一无所知,并且默认为String,除非由显式Converter 另行指定。理论上,在ParameterizedType#getActualTypeArguments() 的帮助下使用令人讨厌的反射黑客是可能的,但 JSF/EL 开发人员可能有他们不这样做的原因。
您确实需要为此明确定义一个转换器。由于 JSF 已经内置了 EnumConverter(在这种特殊情况下不能独立使用,因为您必须在运行时指定枚举类型),您可以按如下方式扩展它:
package com.example;
import javax.faces.convert.EnumConverter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="securityRoleConverter")
public class SecurityRoleConverter extends EnumConverter {
public SecurityRoleConverter() {
super(SecurityRole.class);
}
}
并按如下方式使用:
<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
或
<h:selectManyCheckbox value="#{userController.roles}">
<f:converter converterId="securityRoleConverter" />
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
更通用(和 hacky)的解决方案是将枚举类型存储为组件属性。
package com.example;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="genericEnumConverter")
public class GenericEnumConverter implements Converter {
private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType";
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value instanceof Enum) {
component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass());
return ((Enum<?>) value).name();
} else {
throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass()));
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE);
try {
return Enum.valueOf(enumType, value);
} catch (IllegalArgumentException e) {
throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType));
}
}
}
它可用于使用转换器 ID genericEnumConverter 的各种 List<Enum>。对于List<Double>、List<Integer> 等,可以使用内置转换器javax.faces.Double、javax.faces.Integer 等。由于无法从视图侧指定目标枚举类型(Class<Enum>),内置枚举转换器顺便说一句不合适。 JSF 实用程序库OmniFaces 正好提供了这个转换器out the box。
请注意,对于普通的Enum 属性,内置的EnumConverter 已经足够了。 JSF 将使用正确的目标枚举类型自动实例化它。