簡體   English   中英

Java:如何返回泛型類型

[英]Java: How to return a generic type

我們有一個服務調用各種 rest 端點,並將 JSON 響應轉換為 object。

我們已經看到它在 java 中完成,服務可以返回泛型類型,但無法弄清楚語法。

假設我們有一堆模型用於不同的 API 響應,以及一種調用端點並返回其中一個的方法。 例如

public class MyServiceImpl implements MyService{
    @Override
    public <T> T doGet( String endpoint) { 
        :
        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        :
        Gson gson = new Gson();
        T model = new T();
        model = gson.fromJson(response.getBody(), model.class));
        return(model);
    }

並因此稱為:

SomeModel model = myService.doGet("https://somesite.com/someendpoint")

顯然,上面的代碼是行不通的,因為你不能做“new T()”

httpClient 有一個內置的方法來做到這一點,而不是將響應作為字符串返回,但我們不能使用它有兩個原因:

  1. 我們需要記錄原始響應字符串(這必須在任何可能失敗的 json object 映射之前完成)
  2. 它的“REST”,因此返回 400、404 等導致異常且沒有映射的狀態代碼,但我們仍然需要讀取 json 休止符並將其轉換為 object(在這種情況下有錯誤字段)

正如Yuliya Sheludyakova 提到的,你不能做new T(); 因為javac由於 generics 擦除而缺少類型信息。 即使此時已知類型和構造函數,也不需要創建新的 object,因為 Gson 在反序列化時返回的 object(Yuliya Sheludyak 也提到了這一點)。

What you can do is providing the type (an instance of java.lang.reflect.Type , java.lang.Class is one of them with type-limited capabilities) to the fromJson method invocation so that Gson would deserialize the payload into an object提供的類型。

public interface IService {

    @Nullable
    <T> T doGet(@Nonnull URL url, @Nonnull Type type)
            throws IOException;

    @Nullable
    <T> T doGet(@Nonnull URL url, @Nonnull TypeToken<? extends T> typeToken)
            throws IOException;

}
final class Service
        implements IService {

    // Gson instances are thread-safe and may be expensive on instantiation
    private static final Gson gson = new Gson();

    // Unsafe: the return type T and Type type are not bound to each other
    @Nullable
    @Override
    public <T> T doGet(@Nonnull final URL url, @Nonnull final Type type)
            throws IOException {
        // Prefer not using string buffers that may be very expensive for large payloads
        // Streams are much cheaper
        try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(doGet(url))) ) {
            return gson.fromJson(jsonReader, type);
        }
    }

    // Safe: javac can detect if the return type and the type token are bound
    @Nullable
    @Override
    public <T> T doGet(@Nonnull final URL url, @Nonnull final TypeToken<? extends T> typeToken)
            throws IOException {
        // Prefer not using string buffers that may be very expensive for large payloads
        // Streams are much cheaper
        try ( final JsonReader jsonReader = new JsonReader(new InputStreamReader(doGet(url))) ) {
            // TypeToken.getType() is guaranteed to provide a correct bound type
            return gson.fromJson(jsonReader, typeToken.getType());
        }
    }

    private static <T> T doGet(@Nonnull final URL url) {
        throw new AssertionError("Stub! " + url);
    }

}

安全方法的使用示例:

private static final TypeToken<List<User>> userListTypeToken = new TypeToken<List<User>>() {};

public static void main(final String... args)
        throws IOException {
    final IService service = new Service();
    final List<User> users = service.doGet(new URL("http://localhost:8080/users"), userListTypeToken);
    for ( final User user : users ) {
        System.out.println(user);
    }
}

請注意,Spring RestTemplate、Retrofit 和其他庫也是如此,並且可能值得在您的代碼中使用這些庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM