我正在使用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();
时
-
resource.getClass()
将返回Class<? extends Resource>
为Resource
是删除T
-
Result.builder(resource.getClass())
方法将返回Builder<? extends Resource>
。 -
Result.builder(resource.getClass()).build()
将返回Result<? extends Resource>
。
T
的类型信息在步骤 1 中丢失。因此,编译错误显示为返回类型不兼容。可以进行以下更改来解决该错误。
- 更改构建器方法以接受
T
实例而不是Class<T>
正如您评论中所述。 - 将调用者更改为传递
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>();
}
...
关于java - 如何从静态工厂方法绑定(bind)静态嵌套类的泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58091075/