自限定類型聽起來很複雜,但其實是為了防止子類搞亂類型參數。假設我們有一個泛型類 A,可以接收任何類型的參數:
class A<T> {
T property;
void setProperty(T t) { property = t; }
T getProperty() { return property; }
}
這裡 A 可以用任何類型的參數,比如 A<Integer> 或 A<String>。如果我們有一個類 B,想繼承 A,但只想讓 A 裡面的類型參數固定為 B,通常我們會這麼寫:
class B extends A<B> {}
這個模式叫奇異遞歸泛型。說白了,就是 B 繼承了 A,而 A 裡面又用了 B 作為類型參數。
問題出在哪?#
問題是,這種寫法並不能強制規定一定要用 B 自己作為類型參數。比如有人可以這麼寫:
class C {}
class B extends A<C> {}
這樣,B 繼承了 A,但類型參數用了 C,完全打破了我們想要的規則。
自限定類型的解決方案#
為了解決這個問題,我們就用自限定類型:
class A<T extends A<T>> {}
這麼一改,T 這個泛型參數就被強制要求必須是繼承自 A<T> 的類。換句話說,子類在繼承 A 的時候,必須把自己作為類型參數傳進去。
所以現在:
class C {}
class B extends A<C> {} // 編譯失敗
上面的代碼會編譯失敗,因為 C 沒有繼承自 A<C>。而下面的代碼是合法的:
class B extends A<B> {} // 編譯通過
這樣一來,我們就保證了 B 繼承 A 時,類型參數只能是 B 自己。
自限定類型在實際中的應用:MyBatis-Plus Wrapper#
MyBatis-Plus 是一個流行的 ORM 框架,而 Wrapper 是它中一個常用的工具類,用於構建查詢條件。Wrapper 類及其子類(如 QueryWrapper、UpdateWrapper)在實現中大量使用了自限定類型,確保返回值類型與調用者保持一致,從而實現流式 API(fluent API)的調用。
Wrapper 的定義#
我們可以看一下 AbstractWrapper 類的簡化定義:
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 的簡化實現:
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>> {
// 這裡可以定義額外的查詢條件
}
QueryWrapper 繼承自 AbstractWrapper,並將 QueryWrapper<T> 作為第三個泛型參數 This 傳遞。這意味著,當我們調用 eq 或 like 等方法時,返回值的類型仍然是 QueryWrapper<T>,保證了流式 API 的一致性。
示例代碼#
假設我們有一個 User 表,並想通過 QueryWrapper 構建查詢條件:
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