需要了解语法类Builder <t extended =“” builder <t =“” >>

时间:2018-08-08 16:01:53

标签: java generics

我正在关注“有效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>有什么影响?

4 个答案:

答案 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的实现者的编译时检查。