Общий класс и метод - PullRequest
       16

Общий класс и метод

2 голосов
/ 12 декабря 2011

Я работаю над системой нумерации страниц для своего проекта Java, и я хотел бы сделать ее общей для моих различных моделей JPA.

Проблема (насколько я знаю) заключается в том, что если яиспользовать дженерики, мне придется каким-то образом привести возвращаемое окончательное значение для работы с ним.Как я могу избежать этого?

Вот мой код (абсолютно не универсальный!):

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;
    }
}

Я хотел бы избавиться от этих PicturesPaginator и PicturesPage и сделатьон универсальный, но метод list() из абстрактного класса Page возвращает возвращаемый универсальный список (List<T> или List<GenericModel>, поскольку я использую Play здесь).То, что я ожидал бы, это list() метод, чтобы вернуть правильный список, иначе List<Picture> в моем случае.Возможно ли это?

Примечание: у меня теперь есть модуль для разбивки на страницы в Play!Framework, мой вопрос в основном для того, чтобы понять больше о java:)

Большое спасибо за вашу помощь, я очень ценю!

Ответы [ 5 ]

3 голосов
/ 12 декабря 2011

Вы можете просмотреть исходный код моего модуля Play - Pagination, чтобы увидеть, как я справляюсь с подобными вещами. Я положил источник на github .

Что вы хотите сделать, так это сделать страницу общей и, возможно, не абстрактной:

public class Page<T> {
  public List<T> list() {}
}

И вместо PicturesPage вы можете просто сделать:

new Page<Picture>()

Интерфейс Paginator также необходимо будет обобщить:

public interface Paginator {
    public Page<T> page(Integer page);
}

Генерировать PicturesPaginator будет сложнее, так как вы вызываете методы класса Picture. Реализация дженериков в Java стирает типы во время выполнения, поэтому вам придется иметь дело с токенами типов и отражением.

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> {}
1 голос
/ 12 декабря 2011

Если я вас правильно понимаю, ваша проблема с введением дженериков здесь выглядит следующим образом:

this.query = Picture.find(query, params);

AFAIK, вы не можете напрямую вызывать статические методы для универсальных типов, поэтому вам придется использовать отражение здесь. В этом случае вам может потребоваться передать класс типа параметра в качестве параметра (или получить его из данных отражения, если он существует), найти метод, который вы хотите вызвать, и вызвать его.

Это примерно так:

 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);
 }

Редактировать

В качестве альтернативы передайте объект поиска в общий Paginator и заставьте его реализовать интерфейс, обеспечивающий методы find(...) и count(...):

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);
    ...
  }

  ...
}
1 голос
/ 12 декабря 2011

Если я вас правильно понимаю, может помочь следующее:

Сделать Pace универсальным.Поверните

public abstract class Page {
    protected List<Picture> pictures;

    public List<Picture> list() {
        return pictures;
    }

на:

public abstract class Page<Element> {
    protected List<Element> elements;

    public List<Element> list() {
        return elements;
    }

, чем сделать PicturesPage конкретным:

public class PicturesPage extends Page<Picture> {
1 голос
/ 12 декабря 2011

Я не уверен, что это то, что вам нужно, но вы можете преобразовать класс Page в это:

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;
    }
1 голос
/ 12 декабря 2011

Что я ожидал бы, так это метод list (), который возвращает правильный List, иначе List в моем случае.Возможно ли это?

Это не просто возможно, это то, что вы автоматически получаете, если правильно используете дженерики.Если вы объявите

public class Page<T extends GenericModel>{
    protected List<T> items;

    public List<T> list() {
        return items;
    }
}

и будете использовать его следующим образом:

page = new Page<Picture>();

, тогда page.list() фактически вернет List<Picture>, потому что T - это типпараметр , который заменяется конкретным типом при объявлении Page.

...