Generic Class and Method - java

I'm working on a pagination system for my java project, and I'd like to make it generic for my various JPA Models.
The problem (as far as I know) is that if I use generics, I will have to somehow cast the returned final value to work on it. How can I avoid that ?
Here's my code so far (absolutely not generic!) :
public interface Paginator {
public void setLimit(Integer limit);
public Page page(Integer page);
}
public class PicturesPaginator implements Paginator {
private Integer limit = 10;
private JPAQuery query;
private Long quantity;
public PicturesPaginator(String query, Object... params) {
this.query = Picture.find(query, params);
this.quantity = Picture.count(query, params);
}
#Override
public void setLimit(Integer limit) {
this.limit = limit;
}
#Override
public PicturesPage page(Integer page) {
if (page == null)
page = 1;
List<Picture> pictures = query.fetch(page, limit);
return new PicturesPage(pictures, quantity, page, limit);
}
}
public abstract class Page {
protected List<Picture> pictures;
protected Long quantity;
protected Integer page;
protected Integer limit;
public List<Picture> list() {
return pictures;
}
public Long count() {
return quantity;
}
public boolean hasNext() {
return (page * limit > quantity);
}
public boolean hasPrevious() {
return (page != 1);
}
public boolean hasOtherPages() {
return (hasNext() || hasPrevious());
}
public Integer nextPageNumber() {
if (!hasNext())
return null;
return (page + 1);
}
public Integer previousPageNumber() {
if (!hasPrevious())
return null;
return (page - 1);
}
public Integer currentPageNumber() {
return page;
}
}
public class PicturesPage extends Page {
public PicturesPage(List<Picture> pictures, Long quantity, Integer page, Integer limit) {
this.pictures = pictures;
this.quantity = quantity;
this.page = page;
this.limit = limit;
}
}
I would like to get rid of those PicturesPaginator and PicturesPage and make it generic, but the list() method from the abstract class Page would return a generic List (List<T> or List<GenericModel> since I use Play here).
What I would expect is this list() method to return the correct List, aka List<Picture> in my case. Is this possible ?
Note: I now there is a module for pagination in Play! Framework, my question is mainly for understanding more about java too :)
Thank you very much for your help, I really appreciate!

You can view my Play--Pagination module's source code to see how I handle this type of thing. I put my source on github.
What you want to do is make Page generic as well, and probably non-abstract:
public class Page<T> {
public List<T> list() {}
}
And instead of PicturesPage you could just do:
new Page<Picture>()
The Paginator interface would also need to be generified:
public interface Paginator {
public Page<T> page(Integer page);
}
Generifying PicturesPaginator would be harder since you invoke methods on the Picture class. Java's generics implementation erases types at runtime, so you'll have to deal with type tokens and reflection.
public abstract class GenericPaginator<T> {
public GenericPaginator() {
Class<T> typeToken = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
// use reflection to invoke the finders methods
}
}
public class PicturesPaginator extends GenericPaginator<Picture> {}

What I would expect is this list() method to return the correct List,
aka List in my case. Is this possible?
That's not just possible, that's what you automatically get if you use generics correctly. If you declare
public class Page<T extends GenericModel>{
protected List<T> items;
public List<T> list() {
return items;
}
}
and use it like this:
page = new Page<Picture>();
then page.list() will in fact return a List<Picture>, because T is a type parameter that is replaced by a concrete type when a Page is declared.

If I understand you correctly, the following may help:
Make Pace generic. Turn
public abstract class Page {
protected List<Picture> pictures;
public List<Picture> list() {
return pictures;
}
to:
public abstract class Page<Element> {
protected List<Element> elements;
public List<Element> list() {
return elements;
}
Than make PicturesPage concret:
public class PicturesPage extends Page<Picture> {

I'm not sure if this is what you require, but you can convert the Page class to this:
public abstract class Page<T> {
protected List<T> pictures;
protected Long quantity;
protected Integer page;
protected Integer limit;
public List<T> list() {
return pictures;
}
public Long count() {
return quantity;
}
public boolean hasNext() {
return (page * limit > quantity);
}
public boolean hasPrevious() {
return (page != 1);
}
public boolean hasOtherPages() {
return (hasNext() || hasPrevious());
}
public Integer nextPageNumber() {
if (!hasNext())
return null;
return (page + 1);
}
public Integer previousPageNumber() {
if (!hasPrevious())
return null;
return (page - 1);
}
public Integer currentPageNumber() {
return page;
}

If I understand you correctly, your problem with introducing generics here are lines like this:
this.query = Picture.find(query, params);
AFAIK you can't invoke static methods on generic types directly, so you'd have to use reflection here. In that case you might have to pass the class of the parameter type as a parameter (or get it from reflection data if it exists), find the method you want to invoke and invoke it.
It's something like this:
Class<T> clazz;
public Paginator (Class<T> clazz) {
this.clazz = clazz;
//note that you need to handle exceptions, I'll leave them out for brevity
Method findMethod = clazz.getMethod("find", String.class, Array.class );
this.query = findMethod.invoke(null, query, params);
}
Edit
As an alternative, pass a "finder" object to the generic Paginator and make it implement an interface that provides the find(...) and count(...) methods:
interface Finder<T> {
JPAQuery find( String query, Object... params);
Long count( String query, Object... params);
}
class Paginator<T, F extends Finder<T>> {
public Paginator(F finder, String query, Object... params) {
this.query = finder.find(query, params);
...
}
...
}

Related

Generifying array type such as Object[].class

I am trying to generify following class:
public class FooService {
private Client client;
public Foo get(Long id) {
return client.get(id, Foo.class);
}
public List<Foo> query() {
return Arrays.asList(client.get(Foo[].class));
}
}
Everything is alright except Foo[].class:
public abstract class BaseService<T, I> {
private Client client;
private Class<T> type;
public BaseService(Class<T> type) {
this.type = type;
}
public T get(I id) {
return client.get(id, type);
}
public List<T> query() {
return Arrays.asList(client.get(/* What to pass here? */));
}
How can I solve this issue without passing Foo[].class in the constructor like I have done with Foo.class?
Java lacks facilities to obtain an array class from element class directly. A common work-around is to obtain the class from a zero-length array:
private Class<T> type;
private Class arrType;
public BaseService(Class<T> type) {
this.type = type;
arrType = Array.newInstance(type, 0).getClass();
}
You can now pass arrType to the client.get(...) method.
Why don't you do something like this:
public class Client<T> {
T instance;
T get(long id) {
return instance;
}
List<T> get(){
return new ArrayList<>();
}
}
class FooService<T> {
private Client<T> client;
public T get(Long id) {
return client.get(id);
}
public List<T> query() {
return client.get();
}
}

SonarLint V3: Fields in a "Serializable" class should either be transient or serializable for List interface

My question is very similar to this except that this issue I have encountered in SonarLint V3 (squid:S1948).
My code is :
public class Page<T> implements Serializable {
Summary summary;
List<T> elements;
public Page() {
summary = new Summary();
}
public List<T> getItemsReceived() {
return elements;
}
public void setItemsReceived(List<T> list) {
this.elements = list;
}
public Summary getSummary() {
return summary;
}
public void setSummary(Summary summary) {
this.summary = summary;
}
}
The Summary Object implements serializable.
public class Summary implements Serializable {
int offset;
int limit;
long totalElements;
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public long getTotalNumberOfElements() {
return totalElements;
}
public void setTotalNumberOfElements(long totalNumberOfElements) {
this.totalElements = totalNumberOfElements;
}
}
Now, If I replace List by ArrayList , then another warning in SonarLint arises that we should be using interface instead of implementation classes.
I think this might be resolved in SonarQube but for SonarLint I don't know.
Is this a bug or am I doing something wrong ?
SonarLint is right. The problem is that there is no guarantee that elements field is serializable. You need to add type bound on T type like this
public class Page<T extends Serializable> implements Serializable {}
This way the list will be serializable if implementation chosen for it is serializable (which is true for standard collection types in Java).

How to consume Page<Entity> response using Spring RestTemplate

I'm using spring data (mongoDb) and I've got my repository:
public interface StoriesRepository extends PagingAndSortingRepository<Story, String> {}
Then i have a controller:
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Page<StoryResponse>> getStories(Pageable pageable) {
Page<StoryResponse> stories = storiesRepository.findAll(pageable).map(StoryResponseMapper::toStoryResponse);
return ResponseEntity.ok(stories);
}
Everything works fine, but I can't consume my endpoint using RestTemplate getForEntity method:
def entity = restTemplate.getForEntity(getLocalhost("/story"), new TypeReference<Page<StoryResponse>>(){}.class)
What class should I provide to successfully deserialize my Page of entities?
new TypeReference<Page<StoryResponse>>() {}
The problem with this statement is that Jackson cannot instantiate an abstract type. You should give Jackson the information on how to instantiate Page with a concrete type. But its concrete type, PageImpl, has no default constructor or any #JsonCreators for that matter, so you can not use the following code either:
new TypeReference<PageImpl<StoryResponse>>() {}
Since you can't add the required information to the Page class, It's better to create a custom implementation for Page interface which has a default no-arg constructor, as in this answer. Then use that custom implementation in type reference, like following:
new TypeReference<CustomPageImpl<StoryResponse>>() {}
Here are the custom implementation, copied from linked question:
public class CustomPageImpl<T> extends PageImpl<T> {
private static final long serialVersionUID = 1L;
private int number;
private int size;
private int totalPages;
private int numberOfElements;
private long totalElements;
private boolean previousPage;
private boolean firstPage;
private boolean nextPage;
private boolean lastPage;
private List<T> content;
private Sort sort;
public CustomPageImpl() {
super(new ArrayList<>());
}
#Override
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
#Override
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
#Override
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
#Override
public int getNumberOfElements() {
return numberOfElements;
}
public void setNumberOfElements(int numberOfElements) {
this.numberOfElements = numberOfElements;
}
#Override
public long getTotalElements() {
return totalElements;
}
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
}
public boolean isPreviousPage() {
return previousPage;
}
public void setPreviousPage(boolean previousPage) {
this.previousPage = previousPage;
}
public boolean isFirstPage() {
return firstPage;
}
public void setFirstPage(boolean firstPage) {
this.firstPage = firstPage;
}
public boolean isNextPage() {
return nextPage;
}
public void setNextPage(boolean nextPage) {
this.nextPage = nextPage;
}
public boolean isLastPage() {
return lastPage;
}
public void setLastPage(boolean lastPage) {
this.lastPage = lastPage;
}
#Override
public List<T> getContent() {
return content;
}
public void setContent(List<T> content) {
this.content = content;
}
#Override
public Sort getSort() {
return sort;
}
public void setSort(Sort sort) {
this.sort = sort;
}
public Page<T> pageImpl() {
return new PageImpl<>(getContent(), new PageRequest(getNumber(),
getSize(), getSort()), getTotalElements());
}
}
I know this thread is a little old, but hopefully someone will benefit from this.
#Ali Dehghani's answer is good, except that it re-implements what PageImpl<T> has already done. I considered this to be rather needless. I found a better solution by creating a class that extends PageImpl<T> and specifies a #JsonCreator constructor:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.company.model.HelperModel;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import java.util.List;
public class HelperPage extends PageImpl<HelperModel> {
#JsonCreator
// Note: I don't need a sort, so I'm not including one here.
// It shouldn't be too hard to add it in tho.
public HelperPage(#JsonProperty("content") List<HelperModel> content,
#JsonProperty("number") int number,
#JsonProperty("size") int size,
#JsonProperty("totalElements") Long totalElements) {
super(content, new PageRequest(number, size), totalElements);
}
}
Then:
HelperPage page = restTemplate.getForObject(url, HelperPage.class);
This is the same as creating a CustomPageImpl<T> class but allows us to take advantage of all the code that's already in PageImpl<T>.
As "pathfinder" mentioned you can use exchange method of RestTemplate. However instead of passing ParameterizedTypeReference<Page<StoryResponse>>() you should pass ParameterizedTypeReference<PagedResources<StoryResponse>>(). When you get the response you could retrieve the content - Collection<StoryResponse>.
The code should look like this:
ResponseEntity<PagedResources<StoryResponse>> response = restTemplate.exchange(getLocalhost("/story"),
HttpMethod.GET, null, new ParameterizedTypeReference<PagedResources<StoryResponse>>() {});
PagedResources<StoryResponse> storiesResources = response.getBody();
Collection<StoryResponse> stories = storiesResources.getContent();
Apart from the content storiesResources holds page metadata and links too.
A more step-by-step explanation is available here: https://stackoverflow.com/a/46847429/8805916
If you use spring-cloud-openfeign you can use PageJacksonModule.
Just register PageJacksonModule in your object mapper:
final ObjectMapper mapper = new ObjectMapper()
mapper.registerModule(new PageJacksonModule());
If you looking at this thread, and if you try this answer
https://stackoverflow.com/a/44895867/8268335
You will meet the 2nd problem:
Can not construct instance of org.springframework.data.domain.Pageable
Then I find the perfect solution from here:
https://stackoverflow.com/a/42002709/8268335
I create the class RestPageImpl from the answer above and problem solved.
You can probably use exchange method of restTemplate and get the body from it..
Check the following answer https://stackoverflow.com/a/31947188/3800576.
This might help you

Generics class cast in Java

Hi I have very similar classes (Cars, Clients, Reservations). In those classes i have the same functions like ( add, delete, getItem, getAll and sort).
public class Clients {
List<Client> persons = new ArrayList<Client>();
public void add (Client k) {
persons.add(k);
}
public void delete (Client k) {
persons.remove(k);
}
public Client getKlienet(int id) {
for ( Client k: persons) {
if ( k.getId() == id)
return k;
}
return null;
}
public List<Client> getAllClients() {
List<Client> temp = new ArrayList<Client>();
temp.addAll(persons);
return temp;
}
public List<Client> sortujKontakty() {
Collections.sort(persons);
return persons;
}
I'd like to do with these classes one generic class. But i have a lot of problems..
First with function getId() (this function simply returns value of Id), and with sort() method. Compareto() methods are different for other classes.
So, i done interface:
public interface ManagerInterface <T> {
public void add (T t );
public void delete ( T t);
public T getRecord(int id);
public List<T> getAll();
public List<T> sort();
}
And class:
public class RecordManager<T> implements RecordManagerInterface <T> {
private T id;
public T getId() {
return id;
}
public ArrayList<T> record = new ArrayList<T>();
#Override
public void add(T t) {
record.add(t);
}
#Override
public void delete(T t) {
record.remove(t);
}
#Override
public T getRecord(int id) {
for ( T k: record) {
if ( ((Client) k).getId() == id)
return k;
else if ( ((Person) k).getId() == id)
return k;
else if ( ((Reservation) k).getId() == id)
return k;
}
return null;
}
#Override
public List<T> getAll() {
List<T> temp = new ArrayList<T>();
temp.addAll( record);
return temp;
}
#Override
public List<T> sort() {
Collections.sort(record);
return record;
}
Please for any help.
if you have similar classes, try to create common interface for them,
ie
interface HasId {
int getId();
}
then all what you need is make your classes to implement it, and change your RecordManager to be
public class RecordManager<T extends HasId> implements RecordManagerInterface <T> {
...
}
that means your T has to implement your interface,
so your get getRecord method will be much simpler
public T getRecord(int id) {
for ( T k: record) {
if (k.getId() == id)
return k;
}
return null;
}
EDIT
Lets say your common interface will be
interface CommonInterface<T> extends Comparable<T> {
public int getId();
}
then definition of your class will be, ie
Person implements CommonInterface<Person> {
...
}
your RecordManager
class RecordManager<T extends CommonInterface<T>> implements
RecordManagerInterface<T>{
}
and that should fix all your problems with sort
This is what is called "coding to the interface". Then when you instantiate the objects, make your reference to the interface and the object to the specific class you want to instantiate.
Ex:
Interface example = new Class();
Then use example.getMethod(), example.setMethod(). This will actually call the Class.getMethod() if it has one, as per inheritance.

How to implement a generic wrapper for a ResultSet-like API?

I have an third-party RPC-API that provides an interface similar to that of java.sql.ResultSet (for reading values) and java.sql.PreparedStatement (for writing values). Assume it looks something like this:
public interface RemoteDeviceProxy {
public void setBoolean(Boolean value);
public void setInteger(Integer value);
// ...
public Boolean getBoolean();
public Integer getInteger();
// ...
}
I want to write a wrapper for this API that uses generics to create instances of specific types:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
public RemoteVariable(RemoteDeviceProxy wrappedDevice) {
this.wrappedDevice = wrappedDevice;
}
public T get() {
// should call wrappedDevice.getBoolean() if T is Boolean, etc.
// how to implement?
}
public void set(T newValue) {
// should call wrappedDevice.setBoolean(newValue) if T is Boolean, etc.
// implement using instanceof
}
}
How can I implement the getter in my generic wrapper? I have found this answer which explains a similar scenario in depth, but I am not able to transfer this to my problem. Specifically, when I write this:
public T get() {
Type[] actualTypeArguments = ((ParameterizedType) getClass())
.getActualTypeArguments();
}
I get a compiler error saying I cannot cast to ParameterizedType, and I do not understand why. Can anyone explain how to achieve this?
Here is one way:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
private final Class<T> clazz;
public RemoteVariable(RemoteDeviceProxy wrappedDevice, Class<T> clazz) {
this.wrappedDevice = wrappedDevice;
this.clazz = clazz;
}
public T get() {
if(clazz == Boolean.class){return clazz.cast(wrappedDevice.getBoolean());}
else if(clazz == Integer.class){return clazz.cast(wrappedDevice.getInteger());}
// ...
}
// ...
}
I thought over this quite a while and finally came up with a different approach:
First I added a getter to you RemoteVariable class:
protected RemoteDeviceProxy getWrappedProxy() {
return wrappedProxy;
}
Second I created a builder interface that will be used by a factory later:
public interface RemoteVariableBuilder {
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy);
}
Then I created non generic sub classes for Boolean...
public class RemoteBooleanVariable extends RemoteVariable<Boolean> implements RemoteVariableBuilder {
public RemoteBooleanVariable(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteBooleanVariable(wrappedProxy);
}
#Override
public Boolean get() {
return getWrappedProxy().getBoolean();
}
#Override
public void set(Boolean value) {
getWrappedProxy().setBoolean(value);
}
}
... and Integer ...
public class RemoteIntegerBuilder extends RemoteVariable<Integer> implements RemoteVariableBuilder {
public RemoteIntegerBuilder(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteIntegerBuilder(wrappedProxy);
}
#Override
public Integer get() {
return getWrappedProxy().getInteger();
}
#Override
public void set(Integer value) {
getWrappedProxy().setInteger(value);
}
}
actually eclipse created most of the code once it knew base class and interface.
The final step was to create a factory
public class RemoteVariableFactory {
private static final Map<String, RemoteVariableBuilder> BUILDERS = new HashMap<>();
static {
BUILDERS.put(Boolean.class.getName(), new RemoteBooleanVariable(null));
BUILDERS.put(Integer.class.getName(), new RemoteIntegerBuilder(null));
// add more builders here
}
public static <T> RemoteVariable<T> getRemoteVariable(RemoteDeviceProxy wrappedProxy, Class<T> typeClass) {
RemoteVariableBuilder remoteVariableBuilder = BUILDERS.get(typeClass.getName());
if (remoteVariableBuilder == null) {
return null; // or throw an exception whichever is better in your case
}
return remoteVariableBuilder.buildNewVariable(wrappedProxy);
}
}
Now we are ready to create new RemoteVariables...
RemoteVariable<Boolean> var1 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Boolean.class);
RemoteVariable<Integer> var2 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Integer.class);
To conclude this let's do a quick comparison to the answer of Eng.Fouad:
Disadvantage:
you need to create a new class for every datatype you provide
Advantage:
you only have to add one line to the static block of the factory and not two new if blocks to the getter and setter in RemoteVariable
get and set do not have to work through the if-else-blocks every time

Categories

Resources