自限定类型到底是啥？

自限定类型听起来很复杂，但其实是为了防止子类搞乱类型参数。假设我们有一个泛型类 A，可以接收任何类型的参数：

Copy
class A<T> {
T property;
void setProperty(T t) { property = t; }
T getProperty() { return property; }
}

这里 A 可以用任何类型的参数，比如 A<Integer> 或 A<String>。如果我们有一个类 B，想继承 A，但只想让 A 里面的类型参数固定为 B，通常我们会这么写：

Copy
class B extends A {}

这个模式叫奇异递归泛型。说白了，就是 B 继承了 A，而 A 里面又用了 B 作为类型参数。

问题出在哪？

问题是，这种写法并不能强制规定一定要用 B 自己作为类型参数。比如有人可以这么写：

Copy
class C {}

class B extends A<C> {}

这样，B 继承了 A，但类型参数用了 C，完全打破了我们想要的规则。

自限定类型的解决方案

为了解决这个问题，我们就用自限定类型：

Copy
class A<T extends A<T>> {}

这么一改，T 这个泛型参数就被强制要求必须是继承自 A<T> 的类。换句话说，子类在继承 A 的时候，必须把自己作为类型参数传进去。

所以现在：

Copy
class C {}

class B extends A<C> {} // 编译失败

上面的代码会编译失败，因为 C 没有继承自 A<C>。而下面的代码是合法的：

Copy
class B extends A {} // 编译通过

这样一来，我们就保证了 B 继承 A 时，类型参数只能是 B 自己。

自限定类型在实际中的应用：MyBatis-Plus Wrapper

MyBatis-Plus 是一个流行的 ORM 框架，而 Wrapper 是它中一个常用的工具类，用于构建查询条件。Wrapper 类及其子类（如 QueryWrapper、UpdateWrapper）在实现中大量使用了自限定类型，确保返回值类型与调用者保持一致，从而实现流式 API（fluent API）的调用。

Wrapper 的定义

我们可以看一下 AbstractWrapper 类的简化定义：

Copy
public abstract class AbstractWrapper<T, R, This extends AbstractWrapper<T, R, This>> {
// 假设有一些字段和方法

public This eq(R column, Object val) {
// 实现逻辑
return (This) this;
}

public This like(R column, Object val) {
// 实现逻辑
return (This) this;
}

// 其他条件方法...
}

在这个定义中，This 是一个自限定类型参数，继承自 AbstractWrapper 本身。这意味着，任何继承 AbstractWrapper 的类，都必须将自身作为 This 的类型参数传递。这确保了链式调用返回的仍然是具体的子类类型，而不是基类类型。

QueryWrapper 的实现

接着看一下 QueryWrapper 的简化实现：

Copy
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>> {
// 这里可以定义额外的查询条件
}

QueryWrapper 继承自 AbstractWrapper，并将 QueryWrapper<T> 作为第三个泛型参数 This 传递。这意味着，当我们调用 eq 或 like 等方法时，返回值的类型仍然是 QueryWrapper<T>，保证了流式 API 的一致性。

示例代码

假设我们有一个 User 表，并想通过 QueryWrapper 构建查询条件：

Copy
QueryWrapper<User> query = new QueryWrapper<>();
query.eq("name", "John")
.like("email", "gmail.com");

// 最终执行 SQL 查询
List<User> users = userMapper.selectList(query);

在上面的代码中，每个 eq 和 like 方法返回的都是 QueryWrapper<User> 对象，这样我们就可以继续链式调用其他条件方法。

自限定类型的作用

在这种设计中，自限定类型的作用非常明显：

类型安全：避免了返回不正确类型的情况。比如，确保 QueryWrapper 的 eq 方法返回的仍然是 QueryWrapper，而不是其父类 AbstractWrapper。

链式调用：通过自限定类型的使用，保证了方法链的连贯性，让代码更加简洁、直观。

一句话

自限定类型就是一种让类在继承时必须用自己作为泛型参数的技巧，避免在类型传递中出错。这种写法虽然看着绕，但用在需要严格控制类型的场景下还是很有用的。在实际开发中，像 MyBatis-Plus 这样的框架通过使用自限定类型，实现了更为优雅和类型安全的 API 设计。

此文由 Mix Space 同步更新至 xLog
原始链接为 https://me.liuyaowen.club/posts/default/20240821and1