Apache Thrift中TBase递归泛型接口定义的解析与疑问
Great question—this recursive generic syntax can look intimidating at first, but it’s an intentional design for type safety. Let’s break it down and clarify how the "recursion" terminates when implementing concrete classes.
First, let’s restate the interface definition for clarity:
public interface TBase<T extends TBase<?, ?>, F extends TFieldIdEnum> extends Comparable<T>, Serializable;
What do the generics mean?
T: This is meant to be the concrete class that implements TBase itself. The constraintT extends TBase<?, ?>ensures thatTis a valid TBase implementation (any TBase, regardless of its own generics).F: This is an enum that implementsTFieldIdEnum, representing the field IDs and types for your concrete class (Thrift uses this to handle serialization/deserialization of struct fields).
How to "terminate" the recursion
The recursion here is a compile-time generic constraint loop, not a runtime recursive call. You terminate it by passing your concrete class as the T parameter when implementing TBase.
Let’s walk through a concrete example:
Step 1: Define your field enum
First, create an enum that implements TFieldIdEnum to represent your struct’s fields:
public enum UserFields implements TFieldIdEnum { USER_ID(1, TType.I32), USER_NAME(2, TType.STRING); private final int fieldId; private final byte fieldType; UserFields(int fieldId, byte fieldType) { this.fieldId = fieldId; this.fieldType = fieldType; } @Override public int getThriftFieldId() { return fieldId; } @Override public byte getThriftFieldType() { return fieldType; } }
Step 2: Implement TBase with your concrete class as T
Now create your concrete struct class, using its own class name as the first generic parameter for TBase:
public class User implements TBase<User, UserFields> { private int userId; private String userName; // Implement Comparable<T> (which is Comparable<User> here) @Override public int compareTo(User other) { int idCompare = Integer.compare(this.userId, other.userId); if (idCompare != 0) return idCompare; return this.userName.compareTo(other.userName); } // Implement TBase methods (example: deepCopy returns a User, not a generic TBase) @Override public User deepCopy() { User copy = new User(); copy.userId = this.userId; copy.userName = this.userName; return copy; } // Implement other required TBase methods: isSet, unset, read, write, etc. }
Why this works
When you write User implements TBase<User, UserFields>, the compiler checks that User satisfies T extends TBase<?, ?>—which it does, because it’s implementing TBase directly. The "recursion" is just a self-referential constraint that forces the interface to work with the concrete type, not a vague TBase<?, ?>.
This design has a big benefit: methods like deepCopy() or compareTo() return your concrete type (e.g., User) instead of a generic TBase, so you avoid messy type casts and get compile-time type safety.
内容的提问来源于stack exchange,提问作者Jal

