20

我想在 a 中使用枚举值<h:selectManyCheckbox>。该复选框已正确填充,但是,在选择某些值并提交它们时,它们的运行时类型是String,而不是枚举。我的代码:

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

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

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 方法时,它包含一个字符串类型的列表,而不是枚举类型。有任何想法吗?谢谢!

4

2 回答 2

37

这个问题与枚举没有特别的关系。List对于 JSF 具有内置转换器的其他类型,例如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));
        }
    }

}

它可用于各种List<Enum>使用转换器 ID genericEnumConverter。对于List<Double>, List<Integer>, 等等 , 会使用内置的转换器javax.faces.Double,javax.faces.Integer等等。顺便说一下,内置的枚举转换器不适合,因为无法Class<Enum>从视图侧指定目标枚举类型 (a)。JSF 实用程序库OmniFaces正好提供了这种开箱即用的转换器。

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

于 2010-09-29T16:36:57.447 回答
1

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

泛型擦除是在不破坏旧东西的情况下将泛型放入语言中的一种聪明方法,但现在我们永远生活在这个决定的后果中......

于 2012-08-13T18:15:37.893 回答