自限定類型聽起來很複雜,但其實是為了防止子類搞亂類型參數。假設我們有一個泛型類 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