在尝试调用方法时遇到泛型问题

时间:2017-04-21 08:03:26

标签: java validation generics

我正在尝试为我的Java应用程序开发静态验证方法。我使用带有validate(T data)方法和枚举的验证器对象来存储它们。跟随我的代码片段。

public enum ValidationType {
    TAX_CODE(null); // FIXME

    private final DataValidator<?> validator;

    private ValidationType(DataValidator<?> validator) {
        this.validator = validator;
    }
}

public static <T> void validate(T data, ValidationType dataType) {
    dataType.validator.validate(data);
}

private static abstract class DataValidator<T> {
    public abstract Class<T> getType();
    public abstract void validate(T data);
}

使用Eclipse进行编辑时,我在调用validate方法时遇到错误(行dataType.validator.validate(data);)。

Eclipse说:

  

ValidationUtil.DataValidator类型中的方法validate(capture#3-of?)不适用于参数(T)

我无法理解这个消息。如果可能的话,如何修改或修改代码以使其有效?

提前感谢您提出意见和建议。

2 个答案:

答案 0 :(得分:3)

您收到该错误是因为您使用的是通用通配符,而且您的代码不是类型安全的。

dataType.validatorDataValidator<?>,表示:DataValidator某种未知类型(由?表示)。当您使用某种类型validate()的值调用T时,编译器会抱怨,因为DataValidator接受的类型未知。因此,它无法检查类型T是否属于此DataValidator应该允许的类型。

一种解决方案是不使用通用通配符。

问题很复杂,因为你在enumenums cannot have type parameters(至少还没有 - 这可能成为未来Java版本中的一个功能)。例如,这是不允许的:

public enum ValidationType<T> { // Not valid Java! (at least not in Java 8)
    TAX_CODE(null); // FIXME

    private final DataValidator<T> validator;

    // etc.
}

你可以做的是拥有一个带常量的类:

public final class ValidationType<T> {
    public static final ValidationType<String> TAX_CODE = new ValidationType<>(...);
    public static final ValidationType<Integer> SOMETHING = new ValidationType<>(...);
    public static final ValidationType<Double> ANOTHER = new ValidationType<>(...);

    private final DataValidator<T> validator;

    private ValidationType(DataValidator<T> validator) {
        this.validator = validator;
    }

    public DataValidator<T> getValidator() {
        return validator;
    }
}

答案 1 :(得分:1)

枚举中的字段validator的类型为DataValidator<?>,与DataValidator<T>不匹配。

但是,您可以通过为具有正确签名的字段创建访问器方法来解决此问题:

      public <T> DataValidator<T> getValidator() {
          return (DataValidator<T>)validator;
      }

您需要在此处进行转换,因为您假设T中传递的类型实际上属于使用的枚举常量。

换句话说,系统无法阻止你做愚蠢的事情:

TAX_CODE.getValidator().validate(new HashMap<>());

不会检查TAX_CODE必须是String,它会在ClassCastException的运行时失败。

以下是经过调整的代码:

  public enum ValidationType {
      TAX_CODE(null); // FIXME

      private final DataValidator<?> validator;

      private ValidationType(DataValidator<?> validator) {
          this.validator = validator;
      }

      public <T> DataValidator<T> getValidator() {
          return (DataValidator<T>)validator;
      }
  }

  public static <T> void validate(T data, ValidationType dataType) {
      dataType.getValidator().validate(data);
  }

  private static abstract class DataValidator<T> {
      public abstract Class<T> getType();
      public abstract void validate(T data);
  }

<强>更新

重写的提案:

如果你将DataValidator声明为这样的常量,那么它们将是类型安全的:

public static class Validators {
    public static final DataValidator<String> TAX_CODE_VALIDATOR = ...;
}

用法:

Validators.TAX_CODE_VALIDATOR.validate("12345");