You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Retrofit中复用含page等公共字段的POJO类?

Solutions for Reusing Common Pagination Fields in Retrofit POJOs

Hey there! I totally get the frustration of repeating those pagination fields (page, perpage, total, total_pages) across every POJO. The inheritance issue you ran into with List collections is super common—usually because Retrofit/Gson can’t infer the generic type correctly without a little extra setup. Let’s walk through the most reliable fixes:

This is the cleanest approach. Create a generic base class that holds all your common pagination fields, plus a generic data field to hold the actual response content (including Lists). This lets Gson/Retrofit correctly resolve the type of your collection.

Step 1: Define the Generic Base Response

public class BaseResponse<T> {
    private int page;
    private int perpage;
    private int total;
    private int total_pages;
    private T data;

    // Add getters, setters, or use Lombok's @Data to auto-generate them
}

Step 2: Use It in Your API Calls

You don’t even need to create a separate subclass for each endpoint (though you can if you have endpoint-specific fields). Just specify the exact type of your data in the Retrofit Call:

public interface ApiService {
    // For a list of User objects
    @GET("users")
    Call<BaseResponse<List<User>>> getUsers(
        @Query("page") int page,
        @Query("perpage") int perpage
    );

    // For a single Post object
    @GET("posts/{id}")
    Call<BaseResponse<Post>> getPost(@Path("id") int postId);
}

If you do need endpoint-specific fields, create a subclass that extends the base:

public class UserListResponse extends BaseResponse<List<User>> {
    // Add any unique fields for this endpoint here, e.g.:
    // private String userRoleFilter;
}

Why this works: By explicitly defining the generic type (like List<User>) in the Call, Retrofit and Gson use a TypeToken to resolve the exact collection type, avoiding the "untyped List" parsing issues you saw before.

2. Composition Over Inheritance

If you prefer avoiding inheritance entirely (some developers swear by this for flexibility), create a separate class for pagination metadata and include it as a field in each of your response POJOs.

Step 1: Create a Pagination Metadata Class

public class PaginationInfo {
    private int page;
    private int perpage;
    private int total;
    private int total_pages;

    // Getters and setters
}

Step 2: Include It in Your Response POJOs

public class UserListResponse {
    private PaginationInfo pagination;
    private List<User> data;

    // Getters and setters
}

public class ProductListResponse {
    private PaginationInfo pagination;
    private List<Product> products; // You can even name the data field differently if needed
}

This approach keeps your code decoupled and avoids any inheritance-related edge cases. It’s especially useful if your API responses use different names for the data field (like products instead of data).

3. Custom Gson Type Adapter (Advanced)

If you can’t modify your existing POJO structure (e.g., legacy code), you can create a custom Gson TypeAdapter that automatically injects the pagination fields into all your response classes. This is more complex but works if you need a non-intrusive fix.

Here’s a simplified example:

public class PaginationTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> clazz = (Class<T>) type.getRawType();
        // Check if the class should get pagination fields (e.g., marker interface)
        if (!PaginatedResponse.class.isAssignableFrom(clazz)) {
            return gson.getAdapter(type);
        }

        TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                JsonElement jsonElement = elementAdapter.read(in);
                JsonObject jsonObject = jsonElement.getAsJsonObject();

                // Extract pagination fields and set them on the response object
                T response = delegate.fromJsonTree(jsonElement);
                if (response instanceof PaginatedResponse) {
                    PaginatedResponse paginated = (PaginatedResponse) response;
                    paginated.setPage(jsonObject.get("page").getAsInt());
                    paginated.setPerpage(jsonObject.get("perpage").getAsInt());
                    // Set other pagination fields...
                }

                return response;
            }
        };
    }
}

// Marker interface to identify paginated responses
public interface PaginatedResponse {
    void setPage(int page);
    void setPerpage(int perpage);
    // Add other setter methods for pagination fields
}

Then register this adapter with your Retrofit Gson instance:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new PaginationTypeAdapterFactory())
    .create();

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build();

This is overkill for most cases, but it’s a handy tool if you can’t refactor your POJOs.


内容的提问来源于stack exchange,提问作者scott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:45:34