如何从静态工厂方法绑定静态嵌套类的泛型类型?

时间:2019-09-25 04:16:18

标签: java generics

我正在使用Effective Java中概述的Builder模式的变体,并且对Java泛型的行为感到困惑。

请考虑以下课程:

public class Result<T extends Resource> {
    //final members
    final boolean success;
    final T resource;

    //private constructor
    private Result(Builder<T> builder) {
        success = builder.success;
        resource = builder.resource;
    }

    //getters
    public boolean isSuccess() {
        return success;
    }
    public T getResource() {
        return resource;
    }

    //static factory method to get a builder
    public static <T2 extends Resource> Builder<T2> builder(Class<T2> clazz) {
        return new Builder<T2>();
    }

    //nested Builder class
    public static class Builder<T extends Resource> {
        boolean success;
        T resource;

        private Builder() { }

        public Builder<T> success(boolean success) {
            this.success = success;
            return this;
        }

        public Builder<T> resource(T resource) {
            this.resource = resource;
            return this;
        }

        public Result<T> build() {
            return new Result<T>(this);
        }
    }
}

当我有一个具体的Resource子类时,这似乎很好用:

Result<Device> result = Result.builder(Device.class).build();

但是,当我在另一个类中使用它来定义一个带有Resource的通用子类的方法时,它甚至无法编译:

public <T extends Resource> Result<T> createResult(T resource) {
    return Result.builder(resource.getClass()).build();
}

编译错误为:

  

类型不匹配:无法从Result 扩展到Result

既然T和T2都扩展了Resource,为什么不能弄清楚Result.Builder.build()应该返回通用类型T的Result?

我发现的一种解决方法是放弃静态builder()方法,而改用如下方式:

public <T extends Resource> Result<T> createResult(T resource) {
    return new Result.Builder<T>().build();
}

似乎应该有一种方法可以使用静态工厂方法和隐藏的构造函数...我错过了什么吗?

1 个答案:

答案 0 :(得分:2)

编译错误的原因是由于Object.getClass()方法引起的。根据java doc:

  

返回此对象的运行时类。返回的Class对象是被表示的类的静态同步方法锁定的对象。

实际结果类型是Class,其中| X |是擦除在其上调用getClass的表达式的静态类型。 例如,此代码段中不需要强制转换:

  数字n = 0;
  类c = n.getClass();   

致电return Result.builder(resource.getClass()).build();

  1. resource.getClass()将返回 Class<? extends Resource> ,因为ResourceT
  2. 的擦除
  3. Result.builder(resource.getClass())方法将返回 Builder<? extends Resource>
  4. Result.builder(resource.getClass()).build()将返回 Result<? extends Resource>

在第1步中丢失了T的类型信息。由于返回类型不兼容,因此显示了编译错误。可以进行以下更改以解决该错误。

  1. 更改构建器方法以接受T实例而不是Class<T>作为注释中的状态。
  2. 将调用者更改为将Class<T>而不是Class<? extends Resource>传递给builder方法。

有关更改的代码段:

  // Caller
  // Solution 1
  public <T extends Resource> Result<T> createResult(T resource) {
    return Result.builder(resource).build();
  }

  // Solution 2
  public <T extends Resource> Result<T> createResult(Class<T> resourceClass) {
    return Result.builder(resourceClass).build();
  }


public class Result<T extends Resource> {

  ...
  //Solution 1
  public static <T2 extends Resource> Builder<T2> builder(T2 instance) {
    return new Builder<T2>();
  }

  // static factory method to get a builder
  // Solution 2
  public static <T2 extends Resource> Builder<T2> builder(Class<T2> clazz) {
    return new Builder<T2>();
  }
  ...

参考:Object Java DocErasure of Generic Type