JSF 转换器可以处理实现相同接口的不同类型的对象吗?

Can JSF converter work with objects of different types which implements the same interface, is it possible?

我有不同的对象,它们都实现相同的接口。 <p:selectCheckboxMenu/> 需要 select 能够使用所有这些对象。这些对象的 默认 值和 selected 值放置在相同的 Map<?,?> 中,很少有这样的 Maps 在另一个 Map 中组合在一起。听起来很复杂,但是请看下面的代码,一切都会很清楚。

当我 select 一个对象时,转换器从 MyBean (CDI bean) 中获取所有对象的列表,通过 uuid 所需的对象和 return 查找它,而不会抛出任何异常.当我尝试使用 selected 对象时,问题就开始了。比如里面的这行代码: onObjectChange() 方法来自 MyBean:

List<AllObjects> objects= objectContainer.getControllers().get("Object 1").get("selected");

抛出异常:

 java.lang.ClassCastException: [Ljava.lang.Object; incompatible with java.util.List

确实,当我将鼠标悬停在 objectContainer 上时,我看到它包含 selected=[Ljava.lang.Object;@dba1b6e7} 类型的对象但是当我在 Eclipse 的表达式面板中评估同一行代码时,我得到了所需的值:MyObject1@d8f0f5f8

我一般不明白是否可以做我正在做的事情,即很少有具有相同界面的不同对象可以被 select<p:selectCheckboxMenu/> 实现。如果是,为什么我有这个铸造问题?我的同事说这可能是我的 converter 的问题,我倾向于同意她的看法,但不知道是否正确,如果是,如何解决。

更新:看起来问题不在 Converter 内部,但由于我通过 Collection 动态收集 selected 值,作为值<ui:param/>。我将它作为 List<AllObjects> 传递并作为 Object 返回。然后我可以将它转换为 Object[] 并将其中的每个对象转换为使用内省的适当对象并且它可以工作。但为什么它会改变初始对象?它不应该这样做。

在此先感谢您和我的代码:

这是一个界面:

public interfaces AllObjects{
    public String getName();
}

有多个对象,MyObjectMyObject1MyObject2实现了上面的接口:

public MyObject implements AllObjects{
...
}

这是我的 bean 以及我的对象是如何初始化的:

public MyBean {

    Map<String, Map<String,List<AllObjects>>> objectContainer = new LinkedHashMap<String, Map<String,List<AllObjects>>>();

    public void init(){

        Map<String,List<AllObjects>> nameValuesPairs1 = new LinkedHashMap<String,List<AllObjects>>();
        List<AllObjects> allSelectedObjects1 = new ArrayList<AllObjects>();
        List<AllObjects> allDefaultObjects1 = new ArrayList<AllObjects>();
        nameValuesPairs.put("default",allDefaultObjects1);
        nameValuesPairs.put("selected",allSelectedObjects1);

        Map<String,List<AllObjects>> nameValuesPairs2 = new LinkedHashMap<String,List<AllObjects>>();
        List<AllObjects> allSelectedObjects2 = new ArrayList<AllObjects>();
        List<AllObjects> allDefaultObjects2 = new ArrayList<AllObjects>();
        nameValuesPairs.put("default",allDefaultObjects2);
        nameValuesPairs.put("selected",allSelectedObjects2);

        objectContainer.put("Object 1", nameValuesPairs1);
        objectContainer.put("Object 2", nameValuesPairs2);
    }
    public void onObjectChange(){
        ...
      List<AllObjects> objects= objectContainer .getControllers().get("Object 1").get("selected"); //throws exception
        ...
    }
}

我的 *.xhtml 页面:

<h:panelGroup id="object_panel">

        <ui:repeat id="objects_id" var="object" value="#{myBean.objectContainer.entrySet().toArray()}">

             <p:selectCheckboxMenu 
                            value="#{object.value['selected']}" label="#{object.key}"
                            converter="#{myObjectConverter}" 
                              filter="true" 
                              filterMatchMode="startsWith" 
                              panelStyle="width:250px">
                     <f:selectItems value="#{object.value['default']}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
                     <p:ajax  event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
             </p:selectCheckboxMenu>
        </ui:repeat>

        </h:panelGroup>

和转换器:

public class ChartParameterConverter implements Converter, Serializable  {

        @Inject
        private MyBean myBean;

        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {

            AllObjects result = null;
            ...
            //here to result assigned MyObject1 or MyObject2 type depends on condition and it being returned
            ...
            return result;
        }

        ...
    }

您可以为每个对象创建一个唯一的元组,该元组必须转换为唯一的字符串。您可以将可能的值放在一个数组中。

static Map<String,Object> uniques = new LinkedHashMap<>();
static{
 //you could save the possible values in a Singleton Bean
 uniques.put(key,value)...
}

在您的转换器中

getAsString -> Return a Key from Value
getAsObject -> Return a Value by Key 

好的,看起来 <ui:repeat><p:selectCheckboxMenu> 不能很好地与我使用的 DataModel Map<String,Map<String,List<MyObject>>> 一起使用。我按以下方式更改了 DataModel

public ObjectContainer{
     private String name;
     private List<MyObject> defaultObjects;
     private List<MyObject> selectedObbjects;
}

并将其作为 List<ObjectContainer> 传递给 <ui:param>。所以我的 *.xhtml 页面看起来像下面这样:

         <p:selectCheckboxMenu 
                        value="#{object.selectedObjects}" label="#{object.name}"
                        converter="#{myObjectConverter}" 
                          filter="true" 
                          filterMatchMode="startsWith" 
                          panelStyle="width:250px">
                 <f:selectItems value="#{object.defaultObjects}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
                 <p:ajax  event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
         </p:selectCheckboxMenu>
    </ui:repeat>

现在一切正常。 我扔掉了自定义转换器并使用 Omnifaces's library 中的 SelectItemsConverter。强烈推荐更改,代码变得更加简单易读。