java - 如何从静态工厂方法绑定(bind)静态嵌套类的泛型类型?

标签 java generics

我正在使用Effective Java中概述的构建器模式的变体我对 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);
        }
    }
}

当我有一个具体的资源子类时,这似乎很有效:

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

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

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

编译错误为:

Type mismatch: cannot convert from Result<capture#1-of ? extends Resource> to Result<T>

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

我发现的一个解决方法是放弃静态 builder() 方法并使用类似的方法:

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

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

最佳答案

编译错误的原因是Object.getClass()方法。根据java文档:

Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.

The actual result type is Class where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:

Number n = 0;
Class c = n.getClass();

调用return Result.builder(resource.getClass()).build();

  1. resource.getClass()将返回 Class<? extends Resource> Resource是删除T
  2. Result.builder(resource.getClass())方法将返回 Builder<? extends Resource>
  3. Result.builder(resource.getClass()).build()将返回 Result<? extends Resource>

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

  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 Doc , Erasure of Generic Type

关于java - 如何从静态工厂方法绑定(bind)静态嵌套类的泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58091075/

相关文章:

c# - 我可以将泛型方法限制为多个接口(interface)吗?

java - Decorator方法,Java中的一种Decorator类型

java - java变量的多态或改名

ios - 多层次深度泛型/协议(protocol)的 Swift 泛型和协议(protocol)问题

vb.net - 限制列表(Of T)的大小 - VB.NET

c# - 泛型、继承和转换

java - selenium 中 ApacheHttpClient 类的替代方案是什么

java - Solr Permgen 创建/删除核心时出现异常

java - Jython jmxmp 协议(protocol)支持

TypeScript 泛型 : infer key of key-value return type from function argument