我正在使用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();
}
似乎应该有一种方法可以使用静态工厂方法和隐藏的构造函数...我错过了什么吗?
答案 0 :(得分:2)
编译错误的原因是由于Object.getClass()
方法引起的。根据java doc:
返回此对象的运行时类。返回的Class对象是被表示的类的静态同步方法锁定的对象。
实际结果类型是Class,其中| X |是擦除在其上调用getClass的表达式的静态类型。 例如,此代码段中不需要强制转换:
数字n = 0;
类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>
。 在第1步中丢失了T
的类型信息。由于返回类型不兼容,因此显示了编译错误。可以进行以下更改以解决该错误。
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>();
}
...