我正在关注“有效Java”中的示例,并遇到了以下代码:
abstract static class Builder<T extends Builder<T>>
及其实现:
public static class Builder extends Pizza.Builder<Builder>
为什么将其声明为T extends Builder<T>
而不是T extends Builder
。是否真的需要添加模板<T>
?如果我仅使用Builder<T extends Builder>
有什么影响?
答案 0 :(得分:5)
它称为“通用类型”。该声明意味着Buffer.BlockCopy(ienumerable.ToArray(), 0, toReturn, 0, nRows * nColumns * sizeof(double));
可以是T
子类的任何类型。
实现泛型的目标是在编译时而不是在运行时查找错误。在编译时发现错误可以节省调试Java程序的时间,因为编译时错误更容易查找和修复。
如果仅使用
Builder<T>
有什么影响?
它转换为 raw type 。而且类型安全性消失了。
Builder<T extends Builder>
表示
传入的类Builder<T extends Builder<T>>
必须实现T
接口/扩展Builder
类,并且Builder
的通用参数本身必须是Builder
。 / p>
答案 1 :(得分:2)
我有一些例子表明,实际上差别并不大。我认为OP希望知道T extends Builder<T>
和T extends Builder
之间的区别。
public abstract class Builder2<T extends Builder2> {
//doesn't compile either, because String is not a subtype of Builder2
static class WrongHouseBuilder extends Builder2<String> {}
//all ok
static class RawHouseBuilder extends Builder2 {}
static class HouseBuilder1 extends Builder2<RawHouseBuilder> {}
static class HouseBuilder2 extends Builder2<HouseBuilder1> {}
static class HouseBuilder3 extends Builder2<HouseBuilder2> {}}
现在有Builder<T>
:
public abstract class Builder<T extends Builder<T>> {
//all ok
static class RawCarBuilder extends Builder {}
static class CarBuilder extends Builder<CarBuilder> {}
//ok as well, T doesn't have to be CarBuilder2
static class CarBuilder2 extends Builder<CarBuilder> {}
//doesn't compile because CarBuilder2 is not a subtype of Builder<CarBuilder2>
static class CarBuilder3 extends Builder<CarBuilder2> {}}
T extends Builder<T>
的原因,您可以得到更多的保护,但保护范围不大。
更新
为了澄清,我们不应该使用原始类型。 @Radiodef在评论中提供了一个有趣的示例。并引用该答案中的一句话来帮助您理解它:
简而言之,当使用原始类型时,构造函数,实例方法和非静态字段也会被删除。
答案 2 :(得分:1)
未成年人:对我来说,将Builder
用作interface
而不是abstract class
似乎更自然。这是一种递归类型声明。它用于类型安全,以防止发生以下类似的令人讨厌的事情:
public abstract Builder<T extends Builder<T>> {
T build();
}
public class Entity extends Builder<String>{ // does not compile
@Override
public String build() {
return null;
}
}
public class Entity extends Builder<Entity>{ //ok
@Override
public Entity build() {
return null;
}
}
从我的角度来看,看起来更自然的版本是:
public interface Buildable<T extends Buildable<T>> {
T build();
}
public final class Entity implements Buildable<Entity>{
//other methods
@Override
public Entity build() {
//implementation
}
}
答案 3 :(得分:1)
我看到问题在于<T>
中的Builder<T>
部分。没有这个<T>
,您只会得到一个raw type,您的IDE可能会抱怨。
但是在我的回答中,我想解释T extends Builder<T>
的目的,因为其他答案似乎并未涵盖(也许您已经知道)。
T extends Builder<T>
用于在所有Builder.this
方法中返回适当的Builder
(当然,build()
方法除外)。
我通常将其与受保护的抽象方法(例如T thisInstance()
)一起使用。
示例:
abstract class NamedBuilder<T extends NamedBuilder<T>> {
private String name;
T name(String name) {
this.name = name;
return thisInstance();
}
protected abstract T thisInstance();
}
final class MoreSpecificBuilder extends NamedBuilder<MoreSpecificBuilder> {
@Override
protected MoreSpecificBuilder thisInstance() {
return this;
}
}
通过这种方法,您不必在所有name()
子类中重新定义NamedBuilder
方法就可以返回特定的子类。
没有这样的约束 类型参数T
,您将拥有:
abstract class NamedBuilder {
NamedBuilder name(String name);
}
,您将需要在子类中重写所有此类方法:
final class MoreSpecificBuilder extends NamedBuilder {
@Override
MoreSpecificBuilder name(String name) {
super.name(name);
return this;
}
}
EDIT :类型参数extends Builder<T>
上没有约束 T
:
abstract class NamedBuilder<T> {
// ...
}
这会很好,虽然 这样的设计会不太直观,而且更容易出错。
没有这样的约束,编译器将接受T
(例如String
)之类的任何内容,因此约束仅充当NamedBuilder
的实现者的编译时检查。