简体   繁体   English

集合中具有不同通用值的类型安全

[英]Type safety with different generic values in collection

Suppose I have an interface like this; 假设我有一个这样的接口;

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

And these implementations ; 和这些实现;

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

In ValidatorRegisterer class I have a map; 在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);
    }
}

What I want is to iterate over this map in ValidationManager class with type safety; 我想要的是使用类型安全性在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>();
}

I tried to explain the situation in the last code sample comments. 我试图在最后的代码示例注释中解释这种情况。

Declare as follows to remove warnings : 声明以下内容以删除警告:

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

In this case you are declaring the validator reference that would work on Object type. 在这种情况下,您要声明适用于对象类型的验证器引用。 later you can typecast to Order or String after doing an instanceof test. 稍后,您可以在执行实例测试后将类型转换为Order或String。

You need to make ValidationRegisterer class generic like this: 您需要像这样使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);
    }
}

And then maintain separate lists for these two types of ValidationRegisterer 然后为这两种类型的ValidationRegisterer维护单独的列表

class ValidationManager {
    List<ValidationRegisterer<StringValidator>> strValidationRegisterers;
    List<ValidationRegisterer<OrderValidator>> ordValidationRegisterers;
    ....  
}

I will assume that with "type safety" you mean that you want to be certain that the object returned for a certain path is really of the type that the associated Validator accepts. 我将假定使用“类型安全性”来表示要确定为某个路径返回的对象确实是关联的Validator接受的类型。

One problem is that the type parameter for the Validator is not available at compile time since, as you say yourself, any kind of Validator can be in the map. 一个问题是, Validator的类型参数在编译时不可用,因为正如您自己所说,任何类型的Validator都可以在映射中。

Also, object.getPath(path) will always return an Object which will always need casting at runtime, so the fact that the validate method limits its argument to type T is of little use. 同样, object.getPath(path)将始终返回一个Object ,该Object始终需要在运行时进行强制转换,因此validate方法将其参数限制为T类型的事实没什么用。

So the best you can do is make validation fail fast in case the object is not of the correct type. 因此,您最好的办法是在对象类型不正确的情况下使验证快速失败。

A solution would be to 1. store the Class object for the Validator , 2. let validate accept an Object as parameter and dynamically cast the object to the validator type at the beginning of the validate method. 一种解决方案是1.存储ValidatorClass对象,2.让validate接受一个Object作为参数,并在validate方法的开头将对象动态转换为Validator类型。 This can be done in an abstract base class. 这可以在抽象基类中完成。

Example: 例:

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
    }
}

An alternative solution if you want to keep Object out of the Validator interface would be to let the path be resolved by a type parameterized object that has a reference the validator and performs the dynamic cast, and keep that in your registry map as value: 如果要将Object排除在Validator界面之外,另一种解决方案是让路径由类型化参数化的对象解析,该对象具有对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);
    }
}

You would then have a Map<String, PathValidator<?> in your ValidationRegisterer class. 然后,您的ValidationRegisterer类中将具有Map<String, PathValidator<?>

I'd personally prefer this alternative solution. 我个人更喜欢这种替代解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM