刘耀文

刘耀文

java开发者
github

自限定類型到底是啥?

自限定類型聽起來很複雜,但其實是為了防止子類搞亂類型參數。假設我們有一個泛型類 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 類及其子類(如 QueryWrapperUpdateWrapper)在實現中大量使用了自限定類型,確保返回值類型與調用者保持一致,從而實現流式 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 傳遞。這意味著,當我們調用 eqlike 等方法時,返回值的類型仍然是 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);

在上面的代碼中,每個 eqlike 方法返回的都是 QueryWrapper<User> 對象,這樣我們就可以繼續鏈式調用其他條件方法。

自限定類型的作用#

在這種設計中,自限定類型的作用非常明顯:

  1. 類型安全:避免了返回不正確類型的情況。比如,確保 QueryWrappereq 方法返回的仍然是 QueryWrapper,而不是其父類 AbstractWrapper
  2. 鏈式調用:通過自限定類型的使用,保證了方法鏈的連貫性,讓代碼更加簡潔、直觀。

一句話#

自限定類型就是一種讓類在繼承時必須用自己作為泛型參數的技巧,避免在類型傳遞中出錯。這種寫法雖然看著繞,但用在需要嚴格控制類型的場景下還是很有用的。在實際開發中,像 MyBatis-Plus 這樣的框架通過使用自限定類型,實現了更為優雅和類型安全的 API 設計。

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


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。