基礎篇已覆蓋語法、擦除、通配符。實際框架開發(如 MyBatis Mapper、Spring Data Repository、Jackson 序列化)會遇到:
List 中的 User)?本文聚焦這些「框架級」進階技巧。
利用 **匿名內部類繼承泛型父類**,編譯器會在字節碼 **Signature 屬性** 中保留完整泛型信息,通過反射讀取:
public abstract class TypeReference {
private final Type type;
protected TypeReference() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof ParameterizedType pt) {
this.type = pt.getActualTypeArguments()[0];
} else {
throw new IllegalStateException("必須匿名繼承 TypeReference<具體類型>");
}
}
public Type getType() { return type; }
}
// 使用:Jackson 反序列化 List
List users = mapper.readValue(json, new TypeReference>() {}.getType());
// 或自定義
TypeReference> ref = new TypeReference>() {};
Type actualType = ref.getType(); // 獲取 ParameterizedType: List
讓 Builder 子類返回自身類型,支持鏈式調用不丟失類型:
// 基礎 Builder
public abstract class AbstractBuilder> {
protected T obj;
public B setId(Long id) { obj.setId(id); return self(); }
protected abstract B self();
public abstract T build();
}
// 具體 Builder
public class UserBuilder extends AbstractBuilder {
public UserBuilder() { this.obj = new User(); }
@Override protected UserBuilder self() { return this; }
public UserBuilder setName(String name) { obj.setName(name); return this; }
@Override public User build() { return obj; }
}
// 使用:類型安全鏈式
User u = new UserBuilder().setId(1L).setName("Alice").build(); // 每步返回 UserBuilder
public class JsonUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
/** 通過 TypeReference 保留泛型簽名反序列化 */
public static T readValue(String json, TypeReference ref) {
try {
return MAPPER.readValue(json, MAPPER.getTypeFactory().constructType(ref.getType()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/** 直接傳 Class(非泛型集合) */
public static T readValue(String json, Class clazz) {
try { return MAPPER.readValue(json, clazz); }
catch (IOException e) { throw new UncheckedIOException(e); }
}
}
// 調用
List list = JsonUtil.readValue(json, new TypeReference>() {});
User single = JsonUtil.readValue(json, User.class);
@FunctionalInterface
public interface CheckedFunction {
R apply(T t) throws E;
}
public class Try {
public static Result of(CheckedFunction fn, T input) {
try { return Result.ok(fn.apply(input)); }
catch (Exception e) { return Result.err(e); }
}
}
// 使用:編譯期強制處理受檢異常
Result r = Try.of(this::queryUser, 1L); // queryUser 拋 SQLException
r.ifOk(user -> ...).ifErr(e -> log.error(e));
當無法用 TypeReference(如舊 API)時,約定俗成傳 Class:
public T findById(Class entityClass, Long id) {
return entityManager.find(entityClass, id); // JPA/Hibernate 需要 Class 對象
}
// 調用
User u = dao.findById(User.class, 1L);
| 方式 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| TypeReference 匿名類 | 集合泛型、嵌套泛型 | 類型完整、IDE 提示 | 語法稍繁瑣 |
| 傳 Class | 單一實體類、JPA/反射 API | 簡單直接 | 無法表達 List |
| Method.getGenericReturnType() | 框架掃描 Mapper 接口方法 | 零侵入、自動推導 | 需方法定義在接口/類上 |
| Field.getGenericType() | 框架注入泛型字段 | 同左 | 同左 |
new TypeReference>()
無法獲取實際類型(擦除後為 TypeReference)。List 時,**必須用 TypeReference 或 TypeFactory.constructCollectionType**,直接傳 List.class 只能得到 List。掌握 **TypeReference 捕獲簽名、F-Bounded 鏈式 API、泛型函數式異常處理、Class