使用集合中的不同通用值键入安全性

时间:2013-11-26 08:30:54

标签: java generics

假设我有这样的界面;

interface Validator<T>{
    void validate<T value>    
}

这些实施;

class StringValidator implements Validator<String>{
    void validate<String value>{}
}
class OrderValidator implements Validator<Order>{
    void validate<Order value>{}
}

在ValidatorRegisterer类中,我有一张地图;

class ValidationRegisterer{
    Map<String, Validator> validatorsForPath = new HashMap<String, Validator>();

    public Map<String, Validator> registerers(){
        return validatorsForPath;
    }

    public void register(String path, Validator validator){
        validatorsForPath.put(path, validator);
    }
}

我想要的是在ValidationManager类中使用类型安全迭代这个地图;

class ValidationManager<RootObject>{
    List<ValidationRegisterer> validationRegisterers;

    public ValidationManager(List<ValidationRegisterer> validationRegisterers){
        this.validationRegisterers = validationRegisterers;
    }

    public void validate(RootObject object){
        for(ValidationRegisterer validationRegisterer in validationRegisterers){

            for(String path : validationRegisterer.keySet()){

                Object value = object.getPath(path);
                Validator validator = validationRegisterer.get(path);
                validator.validate(value);
                //this line gets unchecked call to validate(T) warning and I want to get rid of it
                //the problem is validationRegisterers map can contain both StringValidator and OrderValidator, 
                //so the value can be a String or an Order
                //do I have to cast the value to the type of validator's T type?
            }
        }
    }

    Map<String, Validator> validatorsForPath = new HashMap<String, Validator>();
}

我试着在最后的代码示例注释中解释这种情况。

3 个答案:

答案 0 :(得分:0)

声明如下以删除警告:

Validator<Object> validator = validationRegisterer.get(path);

在这种情况下,您将声明可用于对象类型的验证程序引用。 之后,您可以在执行测试实例后对类型或字符串进行类型转换。

答案 1 :(得分:0)

您需要将ValidationRegisterer类设为通用,如下所示:

class ValidationRegisterer<T extends Validator> {
    Map<String, T> validatorsForPath = new HashMap<String, T>();

    public Map<String, T> registerers(){
        return validatorsForPath;
    }

    public void register(String path, T validator){
        validatorsForPath.put(path, validator);
    }
}

然后为这两种类型的ValidationRegisterer

维护单独的列表
class ValidationManager {
    List<ValidationRegisterer<StringValidator>> strValidationRegisterers;
    List<ValidationRegisterer<OrderValidator>> ordValidationRegisterers;
    ....  
}

答案 2 :(得分:0)

我将假设“类型安全”是指您希望确定为特定路径返回的对象实际上是关联的Validator接受的类型。

一个问题是Validator的类型参数在编译时不可用,因为正如您所说,任何类型的Validator都可以在地图中。

此外,object.getPath(path)将始终返回Object,它始终需要在运行时进行转换,因此validate方法将其参数限制为类型T的事实是很少使用。

因此,如果对象的类型不正确,那么您可以做的最好的事情就是快速验证失败。

解决方案是 1.存储Class的{​​{1}}对象, 2.让Validator接受validate作为参数,并在validate方法的开头动态地将对象转换为验证器类型。这可以在抽象基类中完成。

示例:

Object

如果要将interface Validator<T> { void validate(Object value); Class<T> getType(); } abstract class BaseValidator<T> implements Validator<T> { private final Class<T> type; public BaseValidator(Class<T> type) { this.type = type; } public final void validate(Object o) { doValidate(type.cast(o)); // wrong type will fail fast here } public final Class<T> getType() { return type; } protected abstract void doValidate(T value); } class StringValidator extends BaseValidator<String> { public StringValidator() { super(String.class); } protected void doValidate(String value) { // do actual string validation here } } 保留在Object接口之外,另一种解决方案是让路径通过类型参数化对象解析,该对象具有引用验证器并执行动态转换,并将其作为值保存在注册表映射中:

Validator

然后,您的interface Validator<T> { void validate(final T value); } class PathValidator<T> { private final Class<T> type; private final Validator<T> validator; public PathValidator(final Class<T> type, final Validator<T> validator) { this.type = type; this.validator = validator; } public void validate(final RootObject object, final String path) { T value = type.cast(object.getPath(path)); // throws ClassCastException here if not the correct type validator.validate(value); } } 课程中会有Map<String, PathValidator<?>

我个人更喜欢这种替代解决方案。