JSF和类型安全
当我挣扎了好几个小时后,我终于找到了那些烦人的ClassCastException
来自哪里,我认为这是由Hibernate生成的,它是enum
映射。
但它们来自我的JSF视图,我在其中传递了一个List
回到我的支持bean。
我的数据只包含枚举的值: public Role[] getRoles() { return Role.values(); }
public Role[] getRoles() { return Role.values(); }
。
当我在User
-class中测试roles
的setter并得到这个时,我感到非常震惊:
public void setRoles(List paramRoles) { System.out.println(paramRoles.get(0) instanceof Role); //output: false for(Role role : paramRoles){ ...} //crashes with ClassCastException }
将List paramRoles
更改为List paramRoles
工作得很好。
这怎么可能? 这些generics不应该是类型安全的还是与JSF相关的类型擦除会导致整个类型的安全问题?
也不应该将h:selectManyCheckbox
的返回值设为List
,就像我通过f:selectItems
传入的一样?
您正在经历的行为是完全可以预期的。 此外,它与javagenerics有关,与HTTP的工作方式相同。
问题
-
HTTP部分
问题是您不完全了解HTTP的工作原理。 当您通过按下提交按钮提交数据时,由JSF
标记生成的参数,作为一堆复选框,将作为字符串发送并最终作为
request.getParameter("checkboxName");
也作为字符串 。 当然,JSF不知道如何构建模型对象类Role
。 -
generics部分
如您所知,由于java为generics选择了类型擦除以提供向后兼容性,因此有关generics类型的信息基本上是编译类型工件,并且在运行时丢失。 因此,在运行时,
List
会擦除一个简单,好的旧List
。 至于EL是一种运行时语言,它使用Java Reflection API来处理表达式/调用方法,在运行时没有这样的信息可用。 考虑到HTTP部分,JSF尽力并将字符串对象分配给列表,因为它可以隐式地完成。 如果您愿意告诉JSF不这样做,您需要明确地这样做,即通过指定转换器来了解HTTP请求中期望的对象类型 。 -
JSF部分:善后
JSF有一个提供的
javax.faces.Enum
转换器,如果EL知道列表的编译时generics类型,那就是有效的。 但它不知道它。 如果您在Role[] userRoles
对象上进行多项选择,或者如果您使用
的唯一选择并且值绑定到Role userRole
,则没有必要提供转换器。 在这些示例中,将自动调用内置枚举转换器。因此,为了使其按预期工作,您需要提供一个
Converter
,它将“解释”JSF此列表包含哪些类型的值,以及如何从Role
转换为String
,反之亦然。值得注意的是,多选JSF组件中的任何绑定
List<...>
值都是这种情况。
Stack Overflow的参考点
在检查并解决了问题后,我想知道过去是否有人遇到过这个问题并在此处搜索了一些以前的答案。 毫不奇怪,它之前被问过,当然这个问题已经被BalusC解决了。 以下是两个最有价值的参考点:
- JSF 2.0在selectMany菜单中使用enum ;
- 如何在JSF中创建枚举的下拉菜单 ;
- 如何在f:selectItems中为枚举创建和使用通用bean? 。
测试用例和工作转换器的两个例子
下面我提供了一个完全符合您理解的测试用例:除了第三个
组件之外,一切都按预期工作。 由您完全跟踪它以完全消除该问题。
风景:
Many with enum converter
Many with plain converter
Without any converter
Without any converter + array
豆子:
@ManagedBean @RequestScoped public class Q16433250Bean { private List userRoles = new ArrayList ();//getter + setter private List userRoles2 = new ArrayList ();//getter + setter private List userRoles3 = new ArrayList ();//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(); } }
- Hibernate – > ArrayList不能转换为Set
- 附加对象OutputStream时的ClassCastException
- generics,数组和ClassCastException
- java.lang.ClassCastException的含义:someClass与someClass不兼容
- 签名小程序和服务器端控制器之间的通信
- 将HashMap值添加到TreeSet时出错
- 引起:java.lang.ClassCastException:java.sql.Timestamp无法强制转换为java.sql.Date
- Java转换为double to longexception
- ClassCastException将List 转换为Class