自引用泛型(Self-referential generics / F-bounded polymorphism,很多人也叫 CRTP 风格)指:类型参数的上界本身又引用了这个类型参数,典型形态是:

1
class Base<T extends Base<T>> { ... }

含义:T 必须是“某个继承自 Base<T> 的类型”,从而让 Base 在编译期“知道”子类的精确类型。


1 要解决什么问题:父类方法想返回“子类类型”

没用自引用泛型时的问题

1
2
3
4
5
6
7
8
class Base {
Base withName(String n) { return this; }
}
class UserBuilder extends Base { }

UserBuilder b = new UserBuilder();
b.withName("a") // 返回 Base
.withName("b"); // 链式调用类型会退化成 Base

你会失去子类特有方法的链式调用:

1
b.withName("a").onlyInUser(); // 编译不过,因为 withName 返回 Base

用自引用泛型(让返回类型保持为子类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class Base<B extends Base<B>> {
protected abstract B self();

public B withName(String n) {
// ... set field
return self();
}
}

class UserBuilder extends Base<UserBuilder> {
@Override protected UserBuilder self() { return this; }

public UserBuilder onlyInUser() { return this; }
}

现在:

1
2
3
4
new UserBuilder()
.withName("a")
.onlyInUser()
.withName("b");

链式调用始终保持 UserBuilder 类型。


2 它到底“类型上”在约束什么?

这句:

1
B extends Base<B>

表示:B 不是随便的类型,它必须满足:

  • BBase<B> 的子类型

所以 class UserBuilder extends Base<UserBuilder> 合法;
class X extends Base<String> 不合法(String 不是 Base)。

这种约束让 Base 能安全地把 this 视作 B(通过 self() 或强转),从而在 Base 里写出“返回子类”的 API。


3 为什么需要 self()?能不能直接 (B) this

可以强转,但不推荐暴露在公共基类里。

方案 A:强转(常见但有风险)

1
2
3
4
5
6
abstract class Base<B extends Base<B>> {
@SuppressWarnings("unchecked")
protected B self() { return (B) this; }

public B withName(String n) { return self(); }
}

风险在于:如果有人写了“破坏约束”的继承结构(通过原始类型 raw type 绕过),运行期可能 ClassCastException

方案 B:子类实现 self(更稳)

1
2
3
abstract class Base<B extends Base<B>> {
protected abstract B self();
}

子类返回 this,不需要 unchecked cast,更清晰。


4 典型使用场景

4.1 Builder / Fluent API(最常见)

让基类提供通用链式方法(name、id、tags…),子类还能追加自身方法且不中断链式。

4.2 “可比较/可排序”这类自类型约束(JDK 经典)

Comparable 是个“弱化版”案例:

1
class MyType implements Comparable<MyType> { ... }

工具方法里常见约束:

1
static <T extends Comparable<? super T>> T max(List<? extends T> list) { ... }

这也是为了兼容继承关系(? super T)。

4.3 框架基类:返回子类、注册子类、DSL

例如:

  • ORM/Query DSL:Query<T extends Query<T>> where(...)
  • 图结构/节点 API:Node<N extends Node<N>> addChild(N child)

5 多层继承时怎么写?

你可以把“自类型参数”一路传下去:

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Base<B extends Base<B>> {
protected abstract B self();
public B common() { return self(); }
}

abstract class Mid<B extends Mid<B>> extends Base<B> {
public B mid() { return self(); }
}

class Concrete extends Mid<Concrete> {
@Override protected Concrete self() { return this; }
public Concrete only() { return this; }
}

调用:new Concrete().common().mid().only(); 都保持 Concrete


6 常见坑

6.1 原始类型(raw type)会破坏类型安全

1
2
3
class Bad extends Base {   // 使用 raw type
@Override protected Base self() { return this; }
}

这会导致泛型约束失效,可能埋下运行期强转问题。尽量禁止 raw type。

6.2 “写错自类型”导致诡异编译错误

1
class A extends Base<B> { ... } // B 不是 A

自引用泛型的关键是:参数必须是“自己”(或至少保持一致的自类型),一般写成 extends Base<当前类名>

6.3 组合(composition)有时比继承更简单

如果只是想链式配置,有时用组合/返回 this 并配合接口也能做,别为了泛型而泛型。


7 一个更完整的“可复用 Builder 基类”例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Builder<B extends Builder<B>> {
protected String name;
protected int age;

protected abstract B self();

public B name(String n) { this.name = n; return self(); }
public B age(int a) { this.age = a; return self(); }
}

class UserBuilder extends Builder<UserBuilder> {
private String email;

@Override protected UserBuilder self() { return this; }

public UserBuilder email(String e) { this.email = e; return this; }

public User build() { return new User(name, age, email); }
}