5

ClassCastException何時間も格闘した結果、Hibernate によって生成されたものと思われる迷惑な s がどこから来たのかを最終的に見つけました。これはenum-mapping です。

Listしかし、それらは、 fromを渡した私の JSF ビューから来ました。

    <h:selectManyCheckbox value="#{createUserManager.user.roles}"  ... >
        <f:selectItems value="#{createUserManager.roles}"/>
    </h:selectManyCheckbox>

バッキング Bean に戻ります。
私のデータは単に enum: の値で構成されています public Role[] getRoles() { return Role.values(); }。クラスの
セッターをテストしてこれを得たとき、私は本当にショックを受けました:rolesUser

public void setRoles(List<Role> paramRoles) {

    System.out.println(paramRoles.get(0) instanceof Role); //output: false

    for(Role role : paramRoles){ ...} //crashes with ClassCastException
}

List<Role> paramRolesへの変更List<String> paramRolesは完全に機能しました。
これはどのように可能ですか?これらのジェネリックは型安全であるべきではありませんか、それとも JSF に関連する型消去は型安全性全体を殺すのでしょうか?
また、の戻り値h:selectManyCheckboxList<Role>f:selectItems?

4

1 に答える 1

7

あなたが経験している動作は完全に予期されたものです。さらに、HTTP の仕組みと同じように Java ジェネリックに関連しています。

問題

  1. HTTP部分

    問題は、HTTP の仕組みを完全には理解していないことです。送信ボタンを押してデータを送信すると、チェックボックス<h:selectManyCheckbox>の束として生成された JSF タグのパラメーターが文字列として送信され最終的にも文字列として取得されます。もちろん、JSF はモデル オブジェクト クラスを構築する方法を知りません。<input type="checkbox" name="..." value="userRoleAsString">request.getParameter("checkboxName");Role

  2. ジェネリック部分

    Java が下位互換性を提供するためにジェネリックの型消去を選択したという事実により、ジェネリック型に関する情報は基本的にコンパイル型のアーティファクトであり、実行時に失われます。そのため、実行時にList<Role>消去をプレーンで古き良きものにしListます。また、EL が Java Reflection API を使用して式を処理したりメソッドを呼び出したりするランタイム言語である限り、実行時にそのような情報は利用できません。HTTP の部分を考慮して、JSF は最善を尽くして文字列オブジェクトをリストに割り当てます。これは暗黙的にできることのすべてです。JSF に別の方法を指示する場合は、明示的に行う必要があります。つまり、コンバーターを指定して、HTTP リクエストで期待されるオブジェクトのタイプを知る必要があります。

  3. JSF の部分: 余波

    JSFにはjavax.faces.Enumコンバーターが用意されており、ELがリストのコンパイル時のジェネリック型を知っていれば、実際に機能しますRole。しかし、それは知りません。複数選択がオブジェクトに対して行われる場合、またはにバインドされた値で のRole[] userRolesように一意の選択を使用した場合、コンバーターを提供する必要はありません。これらの例では、組み込みの enum コンバーターが自動的に呼び出されます。<h:selectOneMenu>Role userRole

    したがって、期待どおりに機能させるには、このリストが保持する値のタイプと、 からへ、またはその逆Converterの変換を行う方法を JSF に「説明」するを提供する必要があります。RoleString

    List<...>これは、複数選択 JSF コンポーネント内のバインドされた値の場合に当てはまることに注意してください。


Stack Overflow の参照ポイント

問題を調査して解決した後、過去に誰も問題に直面したことがないのではないかと考え、ここで以前の回答を検索しました。当然のことながら、以前にも質問されましたが、もちろん問題は BalusC によって解決されました。以下に、最も価値のある 2 つの参照ポイントを示します。


テスト ケースと動作するコンバーターの 2 つの例

以下に、完全に理解できるようにテスト ケースを示します。3 番目の<h:selectManyCheckbox>コンポーネントを除いて、すべてが期待どおりに動作します。問題を完全に排除するためにそれを完全に追跡するのはあなた次第です。

景色:

<h:form>
    Many with enum converter
    <!-- will be mapped correctly with Role object -->
    <h:selectManyCheckbox value="#{q16433250Bean.userRoles}" converter="roleEnumConverter">
        <f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
    </h:selectManyCheckbox>
    <br/>
    Many with plain converter
    <!-- will be mapped correctly with Role object -->
    <h:selectManyCheckbox value="#{q16433250Bean.userRoles2}" converter="roleConverter">
        <f:selectItems value="#{q16433250Bean.allRoles2}" var="role" itemLabel="#{role.name}" />
    </h:selectManyCheckbox>
    <br/>
    Without any converter
    <!-- will NOT be mapped correctly with Role object, but with a default String instead -->
    <h:selectManyCheckbox value="#{q16433250Bean.userRoles3}">
        <f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
    </h:selectManyCheckbox>
    <br/>
    Without any converter + array
    <!-- will be mapped correctly with Role object -->
    <h:selectManyCheckbox value="#{q16433250Bean.userRoles4}">
        <f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
    </h:selectManyCheckbox>
    <br/>
    <h:commandButton value="Submit" action="#{q16433250Bean.action}"/>
</h:form>

豆:

@ManagedBean
@RequestScoped
public class Q16433250Bean {

    private List<Role> userRoles = new ArrayList<Role>();//getter + setter
    private List<Role> userRoles2 = new ArrayList<Role>();//getter + setter
    private List<Role> userRoles3 = new ArrayList<Role>();//getter + setter
    private Role[] userRoles4;//getter + setter

    public enum Role {

        ADMIN("Admin"),
        SUPER_USER("Super user"),
        USER("User");
        private final String name;

        private Role(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }
    }

    public Role[] getAllRoles() {
        return Role.values();
    }

    public String action() {
        return null;
    }

}

コンバーター:

@FacesConverter("roleEnumConverter")
public class RoleEnumConverter extends EnumConverter {

    public RoleEnumConverter() {
        super(Role.class);
    }

}

@FacesConverter("roleConverter")
public class RoleConverter implements Converter {

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if(value == null || value.equals("")) {
            return null;
        }
        Role role = Role.valueOf(value);
        return role;
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (!(value instanceof Role) || (value == null)) {
            return null;
        }
        return ((Role)value).toString();
    }

}
于 2013-05-08T06:08:43.020 に答える