简体   繁体   中英

Two generic types in java method

I'm using retrofit2 build some synchronous json proxies.

The response of my server is always in the format:

{status:code, something:{complex object}}

so i have:

public class BasicResponse<T> {

    private T data;

    @SerializedName("status")
    private String status;

 }

My parser has a map that dynamically defines the type and create a BasicResponse<T> based on the name of "something" in the server response

Actually everything is working fine and when "something" return a list of object i'm using Arrays to work with.

But I wanted to change the arrays, to any type of collection and I really dont want to change the parser, so i created a method:

private <U> BasicResponse<U> handleResponse(Response<? extends BasicResponse<U>> resp){

        BasicResponse handledResponse = null;


        if (resp != null && resp.isSuccessful() && resp.body() != null) {
            resp.body();

            if(handledResponse.getData().getClass().isArray())


handledResponse.setData(Arrays.asList(handledResponse.getData()));
//a lot of others stuff
return handledResponse;
}

In this method I would like to get some "resp" as BasicResponse<User[]> and convert it on BasicResponse<List<User>>

But as List<User> is different from User[] java can't compile the return and I would need to have two generic types that can or not be the same...

is it possible?

So if I an understood it correctly you want a single method to be capable of handling a mixture of Arrays and atomic elements typed BasicResponse input but the return is always a List typed BasicResponse.

You cannot use type-parameters (U in your code) to tie the component type from the input to the return. The reason is that X and X[] (whatever X is) don't have a common superclass that would exclude any other type of response data.

One way to go about this is to provide the lambda that would translate whatevr the input type is into the output type like so:

private <U,V> BasicResponse<U> handleResponse(final Response<BasicResponse<V>> resp, final Function<U, V> cast) {
   if (resp == null || !resp.isSuccessful() || resp.body() == null)
       return null;
   else 
       return new BasicResponse<>(cast.apply(resp.body().getData()));
}
...
Response<BasicResponse<String[]>> arrayResp = ...;
BasicResponse<List<String>> listBasicResp = 
    handleResponse(arrayResponse, (v) -> Arrays.asList(v));
...
Response<BasicResponse<String>> atomicResp = ...;
BasicResonse<List<String>> listBasicResp2 = 
    handleResponse(atomicResponse, (v) -> Collections.singletonList(v));

Notice that one can mix different component types for the input and result basic-responses as long as one provides the appropriate lambda to do the translation. I would say that most of the time that should not been an issue, in fact nothing will ever prevent the user code to do such a translation even if you don't facilitate a method to do so.

If you want to provide pre-made methods for those particular two cases where the component types are bound then you can simply add a couple of additional handleResponse methods that delegate in the one above and provide the appropriate lambda:

private <U> BasicResponse<List<U>> handleSingleObjectResponse(final Response<BasicResponse<U>> resp) {
     return handleResponse(resp, (v) -> Collections.singletonList(v));
}

private <U> BasicResponse<List<U>> handleArrayResponse(final Response<BasicResponse<U[]>> resp) {
     return handleResponse(resp, (v) -> Arrays.asList(v));
}

Notice that you cannot overload the same method name as after erasure they all share signature: (Response).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM