关于java:尝试调用方法时遇到泛型问题

Troubles with generics while trying to call a method

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);)。

伊柯丽斯说:

The method validate(capture#3-of ?) in the type ValidationUtil.DataValidator is not applicable for the arguments (T)

我不能理解梅萨加。如果可能,如何修复或修改代码以使其正常工作?

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


您会得到这个错误,因为您使用的是通用通配符,并且您的代码不是类型安全的。

dataType.validatorDataValidator,意思是:某种未知类型的DataValidator(由?表示)。当您使用某个类型的T值调用validate()时,编译器会抱怨,因为DataValidator接受的类型未知。因此,它不能检查类型T是否是该DataValidator所允许的类型。

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

问题是复杂的,因为在EDCOX1 17中有这样的事实,枚举不能有类型参数(至少,还没有——这可能成为Java未来版本中的一个特性)。例如,这是不允许的:

1
2
3
4
5
6
7
public enum ValidationType<T> { // Not valid Java! (at least not in Java 8)
    TAX_CODE(null); // FIXME

    private final DataValidator<T> validator;

    // etc.
}

您可以改为使用一个带有常量的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
    }
}


枚举中的字段validatorDataValidator类型,与DataValidator不匹配。

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

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

您将需要在这里强制转换,因为您假定在T中传递的类型实际上属于所使用的枚举常量。

换言之,系统将无法阻止你做一些愚蠢的事情,比如:

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

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

这是调整后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  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声明为这样的常量,那么它们将是类型安全的:

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

用途:

1
Validators.TAX_CODE_VALIDATOR.validate("12345");