在h:selectManyCheckbox中使用枚举

我想在<h:selectManyCheckbox>使用枚举值。 checkbox会正确填充,但是,在select某些值并提交它们时,它们的运行时types是String ,而不是枚举。 我的代码:

 <h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection"> <f:selectItems value="#{userController.rolesSelectMany}" /> </h:selectManyCheckbox> 

UserController类(SecurityRole是一个枚举types):

 public SelectItem[] getRolesSelectMany() { SelectItem[] items = new SelectItem[SecurityRole.values().length]; int i = 0; for (SecurityRole role : SecurityRole.values()) { items[i++] = new SelectItem(role, role.toString()); } return items; } public List<SecurityRole> getRoles() { getCurrent().getRoles(); } public void setRoles(List<SecurityRole> roles) { getCurrent().setRoles(roles); } 

当JSF调用setRoles方法时,它包含一个Stringtypes的列表,而不是枚举types。 有任何想法吗? 谢谢!

这个问题与枚举没有特别的关系。 对于JSF具有内置转换器的其他Listtypes,例如List<Integer>List<Double>等,您将遇到同样的问题。

问题在于EL在运行时运行,并且在运行时会丢失genericstypes信息。 所以实质上,JSF / EL不知道关于List的参数化types的任何东西,并且默认为String除非由显式的Converter明确指定。 从理论上讲,在ParameterizedType#getActualTypeArguments()帮助下使用讨厌的reflection黑客可能是可能的,但是JSF / EL开发者可能有他们不这样做的理由。

你真的需要为此明确地定义一个转换器。 由于JSF已经内置了一个内置的EnumConverter (在这种情况下不能单独使用,因为你必须在运行时指定枚举types),所以你可以像下面这样扩展它:

 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> 

一个更通用的(和哈克)解决scheme是将枚举types存储为组件属性。

 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 genericEnumConverterList<Enum> 。 对于List<Double>List<Integer>等,可以使用内置的转换器javax.faces.Doublejavax.faces.Integer等等。 由于无法从视图侧指定目标枚举types( Class<Enum> ),所以内build的Enum转换器是不合适的。 JSF实用程序库OmniFaces提供了这个转换器。

请注意,对于普通的Enum属性,内置的EnumConverter已经足够了。 JSF将使用正确的目标枚举types自动实例化它。

在某些情况下, List也可以是一个数组SomeType [] ,在这种情况下,不需要显式的转换器。

通用擦除是一种将generics放入语言而不会破坏旧东西的巧妙方式,但是现在我们永远活在这个决定的后果之中。