Java泛型声明SomeClass<T extends SomeClass<T>>的含义是什么?
这种写法的本质是Java的递归类型限定(Recursive Type Bound)
首先解决你最关心的循环定义疑问:
Java编译器对类的解析是先识别类的符号存在,再处理泛型、方法、属性等内部逻辑,你在类定义的泛型边界里引用当前正在定义的类,和你在类里写一个返回当前类实例的方法本质是一样的,都只是引用已经被识别到的类符号,不会因为类还没完全定义完就报错。
你熟悉的Comparable<T>本身就是这种递归设计的典型例子:Comparable接口的声明本身没有强制递归限定,但是业务上要求实现类传入自身作为类型参数,比如public class User implements Comparable<User>,你平时写的T extends Comparable<T>,本质就是要求T必须是实现了Comparable接口的自身类型,和你现在看到的T extends SomeClass<T>逻辑完全一致,只是SomeClass是你自己定义的抽象类而已。
这个写法的核心用途
最主要的作用是让父类的方法可以直接返回子类类型,避免子类调用时手动强转,特别适合链式调用、Builder模式、克隆方法这类场景。
举个实际的例子:
public abstract class SomeClass<T extends SomeClass<T>> implements Cloneable { // 子类构造方法里需要把self赋值为this protected T self; // 父类通用的链式调用方法,直接返回子类类型T public T setId(Long id) { // 业务逻辑省略 return self; } public T setName(String name) { // 业务逻辑省略 return self; } @Override public T clone() throws CloneNotSupportedException { return (T) super.clone(); } }
子类继承时只需要传入自身作为泛型参数即可:
public class User extends SomeClass<User> { public User() { self = this; } // 子类自己的方法 public User setAge(Integer age) { // 业务逻辑省略 return this; } }
使用时可以直接链式调用,不需要做任何类型强转:
User user = new User() .setId(1L) .setName("张三") .setAge(20); User clonedUser = user.clone();
如果没有这个递归泛型限定,父类的setId、setName只能返回SomeClass类型,调用完父类方法后必须强转为User才能调用子类的setAge,代码冗余度会高很多。
额外说明
这个写法存在一个小的约束漏洞:编译器不会强制子类传入自身作为泛型参数,比如你可以写出public class Order extends SomeClass<User>这种不符合约定的代码,但是只要业务上遵守「子类继承时传入自身作为泛型参数」的约定,就不会出现问题。
内容的提问来源于stack exchange,提问作者Chen Ni

