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 you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With