solvingJ solvingJ - 3 months ago 13
Java Question

What Is Best Practice for Return Types using Java Generics and Factory Pattern

I'm trying to learn to use Generics for the first time, a fairly complex way. I'm also using Jackson 2 for Deserialization.

I'm casting in my return statements every time. Can someone tell me if this is wrong? If so, what is a better way to do it?

Here is a simple example class that demonstrates the situation:

class ResourceFactory <T extends Resource>{

List<T> getResources(String path)
{
ResourcesResponse rgRes = new ObjectMapper()
.readValue(response.body().byteStream(), ResourcesResponse.class)

return (List<T>)rgRes.resources
}
}


UPDATE:

Per a comment, here are super-simplified examples of what the other class bodies look like.

class ResourceResponse {
List<Resource> resources
}

class ResourceGeneric extends Resource {
}

class ResourceTypeOne extends Resource {
public String typeOneOnlyProperty
}

class ResourceTypeTwo extends Resource {
public String typeTwoOnlyProperty
}

Answer

Jackson can support generic types if used correctly. Here's how I would do it:

class ResourcesResponse<T extends Resource> {
    List<T> resources;
}

class ResourceFactory<T extends Resource> {
    // Either of these should work; pick your constructor
    private JavaType responseType;
    private TypeReference<ResourcesResponse<T>> responseType;

    // Option A
    // Involves some boilerplate, but keeps the constructor simple
    // Use: new ResourceFactory<ResourceTypeOne>(ResourceTypeOne.class)
    ResourceFactory(Class<T> resourceType) {
        this.responseType = TypeFactory.defaultInstance()
               .constructParametricType(ResourcesResponse.class, resourceType);
    }

    // Option B
    // Cleaner internally, but exposes implementation details
    // Use: new ResourceFactory<ResourceTypeOne>(new TypeReference<ResourcesResponse<ResourceTypeOne>>() {})
    ResourceFactory(TypeReference<ResourcesResponse<T>> responseType) {
        this.responseType = responseType;
    }

    List<T> getResources(String path)
    {
        ResourcesResponse<T> rgRes = new ObjectMapper()
        .readValue(response.body().byteStream(), responseType);

        return rgRes.resources;
    }
}