20

で列挙値を使用したい<h:selectManyCheckbox>。チェックボックスは正しく入力されますが、いくつかの値を選択して送信すると、それらのランタイム タイプはStringenum ではなく になります。私のコード:

<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 メソッドを呼び出すと、enum 型ではなく String 型のリストが含まれます。何か案は?ありがとう!

4

2 に答える 2

37

この問題は列挙型には特に関係ありません。、などListの JSF に組み込みコンバーターがある他のタイプでも同じ問題が発生します。List<Integer>List<Double>

問題は、EL が実行時に動作し、ジェネリック型情報が実行時に失われることです。したがって、本質的に、JSF/EL は のパラメーター化された型について何も認識せず、明示的に指定されていない限りList、デフォルトは になります。理論的には、 の助けを借りて厄介なリフレクション ハックを使用することは可能でしたが、JSF/EL 開発者にはこれを行わない理由があるかもしれません。StringConverterParameterizedType#getActualTypeArguments()

これには、コンバーターを明示的に定義する必要があります。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>

もう少し一般的な (そしてハックな) 解決策は、列挙型をコンポーネント属性として格納することです。

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を使用するあらゆる種類で使用できますgenericEnumConverterList<Double>、などについては、組み込みのコンバーターなどList<Integer>を使用していたでしょう。ちなみに、組み込みの Enum コンバーターは、ビュー側からターゲットの列挙型 (a) を指定できないため、不適切です。JSF ユーティリティ ライブラリOmniFacesは、まさにこのコンバータをそのまま提供しますjavax.faces.Doublejavax.faces.IntegerClass<Enum>

通常のEnumプロパティの場合、ビルトインEnumConverterで十分であることに注意してください。JSF は、適切なターゲット列挙型を使用して自動的にインスタンス化します。

于 2010-09-29T16:36:57.447 に答える
1

場合によっては、リストを配列SomeType []に​​することもできます。この場合、明示的なコンバーターは必要ありません。

ジェネリック消去は、古いものを壊すことなくジェネリックを言語に組み込む賢い方法でしたが、今ではその決定の結果で永遠に生きています...

于 2012-08-13T18:15:37.893 に答える