[英]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.存储
Validator
的Class
对象,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.