2

我们有一个通用“参数”类型的层次结构,带有非通用的具体叶类,这里给出了它的垂直切片:

public interface Parameter<T>
  public T getValue();
  public void setValue(T value);
  …

public abstract class AbstractParameter<T> implements Parameter<T>
  public final T getValue()
  public final void setValue(T value)
  …

public abstract class AbstractNumberParameter<T extends Number> extends AbstractParameter<T> implements NumberParameter<T>
  …

public class IntegerParameter extends AbstractNumberParameter<Integer>
  …

getValue并且setValue不会在AbstractNumberParameteror中被覆盖IntegerParameter

在一种情况下,在 facelet 代码的迭代中使用 IntegerParameter 的实例,该迭代遍历参数列表并根据参数中枚举字段的值有条件地呈现参数的相应视图(这比泛型类型参数更粗略) )。

<p:dataTable id="inputPanel" styleClass="parameter-input-table"
                value="#{metricModuleManager.metricModule.parameters}"
                var="parameter" rowIndexVar="rowIndex">

    ...

    <p:column>
        <h:panelGroup id="inputArea">

        ...

        <p:spinner id="numberInput"
            rendered="#{parameter.type=='NUMBER'}"
            stepFactor="#{parameter.resolution}"
            value="#{parameter.value}"
            parameter="#{parameter.name}"
            validator="#{metricModuleManager.validateParameter}">
            <p:ajax update="numberMessage" />
            <p:message id="numberMessage" for="numberInput" />
        </p:spinner>

        ...

        </h:panelGroup>
    </p:column>
    ...

</p:dataTable>

(使用 PrimeFaces 元素。)

无论微调器的值是否正确转换为设置器参数的类型,开发环境或运行时环境似乎都不同。为了避免以后的 ClassCastException,传递给的值setValue需要是一个Integer实例。就我而言,我观察到一个String被传递给设置器(String如果我理解正确的话,显然是属性的默认类型)。但是,相同的代码似乎在我同事的计算机上运行良好,因此整数值稍后在工作流中的正确位置使用。它似乎也可以在单独的服务器上正常工作。

我和我的同事都使用 JSF 2 和 Tomcat 7 与 Eclipse 集成。

如果这段代码一直不起作用,那么我会假设它与类型擦除有关。但是,不一致的行为让我认为,在这种情况下,至少 JSF 的一些实现能够从有限的类型元数据中提取来确定要使用的正确转换器。看起来实际的Integer类型参数存储在IntegerParameterClass元数据中:

p.getClass().getGenericSuperclass().toString()AbstractNumberParameter<Integer>在调试代码中p产生实例IntegerParameter在哪里。

调用 TreeSet<Long>.contains( Long.valueOf( someLongValue ) )https://stackoverflow.com/a/13866179/516433时的 ClassCastException确定通过类型擦除,具有泛型类型参数的类的直接实例将不会t 携带实际的类型参数,因此在这种情况下,JSF 实现将没有所需的类型信息来选择正确的转换器。但是,这种情况的不同之处在于类型参数被指定为类定义的一部分吗?(https://stackoverflow.com/a/16886880/1867423建议是这种情况。)然后 JSF 实现是否能够使用自省来确定要使用的正确转换器?

什么可以解释环境之间的行为差​​异?我们有些难过。

4

1 回答 1

0

几个月前我做了类似的事情来解决一个不同的问题。我的许多 JPA 实体类都有一个抽象类。该类型的每个实体转换器都具有几乎相同的逻辑,我想为此使用单个转换器。

经过一番研究,我能够为我的实体找到一个抽象转换器的解决方案,我可以在其中放置通用逻辑。唯一的问题是现在我需要通过它的名称来引用它(converter="entity")。这个解决方案是在其他地方找到的(我不记得在哪里),所以我不能在这里获得任何学分。

抽象转换器:

import java.util.HashMap;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public abstract class AbstractConverter implements Converter {

    private static final String key = "any.key.you.want";

    protected Map<String, Object> getViewMap(FacesContext context) {
        Map<String, Object> viewMap = context.getViewRoot().getViewMap();
        Map<String, Object> idMap = (Map) viewMap.get(key);
        if (idMap == null) {
            idMap = new HashMap<String, Object>();
            viewMap.put(key, idMap);
        }
        return idMap;
    }

    @Override
    public final Object getAsObject(FacesContext context, UIComponent c,String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        return getViewMap(context).get(value);
    }

    @Override
    public final String getAsString(FacesContext context, UIComponent c, Object value) {
        if (value != null) {
            String id = getConversionId(value);
            if (id == null || id.isEmpty()) {
                throw new IllegalArgumentException("Object can't be converted.");
            }
            getViewMap(context).put(id, value);
            return id;
        }
        return null;
    }

    public abstract String getConversionId(Object value);
}

混凝土转换器:

import javax.faces.convert.FacesConverter;
import entity.AbstractEntity;

@FacesConverter("entity")
public class EntityConverter extends AbstractConverter {

    @Override
    public String getConversionId(Object value) {

        if (value instanceof AbstractEntity) {
            AbstractEntity entity = (AbstractEntity) value;
            StringBuilder sb = new StringBuilder();
            sb.append(entity.getClass().getSimpleName());
            sb.append("@");
            sb.append(entity.getId());
            return sb.toString();
        }

        return null;
    }
}

假设这可以解决您的问题,您将需要类似 ParameterConverter 注释的东西@FacesConverter("parameter"),然后按如下方式使用它:

<p:spinner id="numberInput"
        rendered="#{parameter.type=='NUMBER'}"
        stepFactor="#{parameter.resolution}"
        value="#{parameter.value}"
        parameter="#{parameter.name}"
        validator="#{metricModuleManager.validateParameter}"
        converter="parameter"
        >
        <p:ajax update="numberMessage" />
        <p:message id="numberMessage" for="numberInput" />
    </p:spinner>
于 2013-07-16T14:01:57.273 回答