Какой универсальный Java должен использоваться в неоднозначных случаях? - PullRequest
4 голосов
/ 01 декабря 2009

У меня возникли некоторые проблемы с миграцией Wicket 1.3 -> Wicket 1.4, но этот вопрос может быть применен и к универсальным Java-приложениям в целом. Миграция привела к появлению сотен предупреждений из ниоткуда - для тех, кто не знаком с Wicket, многие классы Wicket являются производными от общего предка, который стал обобщенным в v1.4 - и я не уверен, какие параметры применять в некоторых случаях в основном разные формы и таблицы. Я думаю, что они могли бы сделать с <?>, <Object> или <Void>, но я не уверен, какой.

<?> мне кажется наиболее подходящим, но есть много мест, где я не могу использовать подстановочный знак. <Object> работает во всех случаях, но меня это беспокоит, потому что он в основном пишет подстановочный знак без использования подстановочного знака, который просто кажется неправильным для части моего мозга. И использование <Void> было предложено в руководстве по миграции Wicket .

Так что же делать в этом случае?


РЕДАКТИРОВАТЬ 2: Я думаю, что мое первое редактирование (теперь в нижней части вопроса) смутило людей, создавая впечатление, будто я просто спрашивал о коллекциях строк. Вот другие примеры и их предупреждения:

public class DocumentProcessor extends Form implements DocumentManagement { ...

Форма необработанного типа. Ссылки на универсальный тип Form должны быть параметризованы

AjaxFallbackDefaultDataTable theTable = new AjaxFallbackDefaultDataTable("theTable", cols, dataProvider, recPerPg);

Несколько маркеров в этой строке
- Безопасность типов: конструктор AjaxFallbackDefaultDataTable (String, List, ISortableDataProvider, int) принадлежит необработанному типу AjaxFallbackDefaultDataTable. Ссылки на универсальный тип AjaxFallbackDefaultDataTable должны быть параметризованы
- AjaxFallbackDefaultDataTable является необработанным типом. Ссылки на универсальный тип AjaxFallbackDefaultDataTable должны быть параметризованы
- AjaxFallbackDefaultDataTable является необработанным типом. Ссылки на универсальный тип AjaxFallbackDefaultDataTable должны быть параметризованы


РЕДАКТИРОВАТЬ: Я надеялся сделать вопрос настолько широким, чтобы не требовался пример кода, но вот некоторые.

List<IColumn> columns = new ArrayList<IColumn>();
columns.add(new PropertyColumn(new Model<String>("Number"), "revisionID"));

Эти предупреждения генерируются:

Несколько маркеров в [первой] строке
- IColumn является необработанным типом. Ссылки на универсальный тип IColumn должны быть параметризованы
- IColumn является необработанным типом. Ссылки на универсальный тип IColumn должны быть параметризованы

Несколько маркеров на [второй] строке
- Безопасность типов: конструктор PropertyColumn (IModel, String) принадлежит необработанному типу PropertyColumn. Ссылки на универсальный тип PropertyColumn должны быть параметризованы
- PropertyColumn является необработанным типом. Ссылки на универсальный тип PropertyColumn должны быть параметризованы

Нет ошибок.

Ответы [ 6 ]

3 голосов
/ 15 мая 2012

Используйте Void , если вы не собираетесь использовать базовый объект модели компонента.

Семантически, он более звучит и лучше передает идея, что дело не в том, что объект модели может быть чем-либо, а в том, что он семантически ничто и никогда не будет использоваться . Ключевое слово Void чаще всего используется в качестве обычного решения в подобных ситуациях.

Если вы собираетесь использовать объект модели и вам все равно , что я не думаю, что вы имели в виду, ** используйте подстановочные знаки там, где можете ** и там, где можете 't (аргументы конструктора и т. д.), либо Void, Object, либо, возможно, какой-либо другой "охватывающий" класс, принимающий решение на основе конкретной семантики вашего компонента и желаемого поведения универсальной типизации (например, в случае конструктора для переменная компонента, вы подумаете о том, что ваш конструктор будет делать с типом Void или Object).

Конечно, это с точки зрения теории «хороших практик программирования», на практике вам не нужно сильно заботиться, хотя такое мышление может помочь вашим товарищам по команде сохранить ваш код и помочь вам лучше понять его, может быть, даже предсказывать ошибки.

Использование символов подстановки везде довольно распространено среди пользователей калитки, возможно, даже более распространено, чем решение, как я предлагал, но это не потому, что символы подстановки являются соглашением, а, скорее всего, просто из-за большинства примеров кода, которые появляются в поисковых системах, предпочитающих подстановочные знаки. Тем не менее, как подсказывает руководство по миграции Void, подстановочные знаки не только менее семантически согласованы, но и не кажутся абсолютным соглашением, даже оспариваемым, как кажется, разработчиками калитки, которые, как нам следует предположить знать достаточно о внутренней работе их типов, чтобы их рекомендации воспринимались всерьез.

1 голос
/ 03 декабря 2009

Семантически, использование <?> означает «я не знаю тип, и мне на самом деле все равно. Использование чего-либо еще устанавливает ожидания в форме ожидаемого содержимого». Практически, <Object> делает то же самое, но заявляет, что вы будете использовать свойства вашего универсального шаблона, которые используют тип параметра.

Так что эмпирическое правило должно быть:

  • если вы работаете только с генетическим объектом, но не с его параметризованным содержимым, используйте <?>, чтобы вы знали, на первый взгляд, параметр не имеет значения для поведения.
  • в любом другом случае используйте наиболее специфический параметр, который охватывает все типы, с которыми работает ваш метод. Крайний случай <Object>, другие включают <? extends SomeTopLevelType>
1 голос
/ 01 декабря 2009

Доступные альтернативы:

  1. Чтобы просто использовать необработанный тип, как в примере кода, просто игнорируйте предупреждения
  2. Использование универсального символа / универсального объекта
  3. Для использования универсального типа extends

Я предполагаю из вашего вопроса, что # 1 не подходит для вас.

Пример для # 2 (подстановочный знак / объект)

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();

OR

List<IColumn<Object>> columns = new ArrayList<IColumn<Object>>();

IMO Я не думаю, что действительно имеет значение, выбираете ли вы ? или Object, и ни один из них не является более правильным, чем другой, по крайней мере, функционально.
Если вас не волнует, что такое дженерик, и вы никогда не получите к нему доступ, то это имеет почти все последствия; хотя подумайте заранее, если это действительно возможно, вы бы использовали дженерики здесь в будущем. Вероятно, это будет иметь место только в том случае, когда в коде перед миграцией вы обнаружили, что вам не нужно ничего вводить из объектов IColumn.

Пример для # 3 (расширяет универсальный)

Создание супертипа или общего интерфейса для всех возможных обобщений типа IColumn. Где
T extends MyType

List<IColumn<T>> columns = new ArrayList<IColumn<T>>();

Я бы основывал свое решение при выборе между 2-м и 3-м методом на основе возможных общих атрибутов для IColumn.

  • Если это ваши собственные классы И вы действительно хотите получить доступ к объектам универсального типа, я бы выбрал третий метод,
  • в противном случае, например, с String или в штучных примитивах, таких как Integer, или, если вы не используете объекты общего типа, я бы выбрал метод 2.

НТН

0 голосов
/ 04 декабря 2009

Вы хотите использовать тип модели, связанной с вашим компонентом. То есть используйте тип, возвращаемый вызовом getModelObject (). Итак, чтобы использовать пример из руководства по миграции:

ListView<Person> peopleListView = new ListView<Person>("people", people) {
    protected void populateItem(ListItem<Person> item) {
        item.add(new Link<Person>("editPerson", item.getModel()){
            public void onClick() {
                Person p = getModelObject();
                setResponsePage(new EditPersonPage(p));
            }
        });
    }
};

С помощью дженериков легко сказать, что это список людей со ссылкой на страницу редактирования, которая использует человека в качестве модели. К сожалению, очень часто в калитке ваши компоненты не имеют связанной с ними модели. В этом случае getModel () вернет значение null, поэтому правильный тип для использования - <Void>, что по сути является заполнителем для null.

DocumentProcessor

public class DocumentProcessor extends Form implements DocumentManagement { ...

если вы не устанавливаете модель для DocumentProcessor, это будет выглядеть так:

public class DocumentProcessor extends Form<Void> implements DocumentManagement {
    public DocumentProcessor(String id) {
        super(id);
        ....

но с моделью DocumentProcessor выглядит примерно так:

public class DocumentProcessor extends Form<Document> implements DocumentManagement {
    public DocumentProcessor(String id, Document doc) {
        super(id, doc);

AjaxFallbackDefaultDataTable

Судя по его конструкторам, AjaxFallbackDefaultDataTable, скорее всего, будет хранить IColumn [] или List в своей модели, но для вашей реализации вы не знаете или не заботитесь о том, что <?> подходит, разница между этим и DocumentProcessor заключается в том, что вы ' Вы расширяете форму и, следовательно, знаете и заботитесь о том, как она использует свою модель.

IColumn

Для примера IColumn / PropertyColumn я собираюсь предположить, что поле revisionID является Long, тогда я бы написал это так:

List<PropertyColumn> columns = new ArrayList<PropertyColumn>();
columns.add(new PropertyColumn<Long>(new Model<String>("Number"), "revisionID"));

Вы можете посмотреть на

Подробнее 1.4 Информация о миграции

Параметр типа пустоты

0 голосов
/ 01 декабря 2009

Я не использовал wicket, и Vodafone блокирует мне доступ к документации по API. Однако, похоже, что вам не хватает многих общих аргументов и вы хотите что-то вроде:

List<IColumn<String>> columns = new ArrayList<IColumn<String>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

Если вы хотите добавить другие IColumn с не связанным родовым аргументом, вам понадобится что-то вроде;

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

Или, если вам нужно получить доступ к свойствам столбца, возможно, что-то вроде:

List<IColumn<String>> strColumns = new ArrayList<IColumn<String>>();
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
PropertyColumn<String> column =
    new PropertyColumn<String>(new Model<String>("Number"), "revisionID");
strColumns.add(column);
columns.add(column);
0 голосов
/ 01 декабря 2009

В предупреждениях говорится, что Интерфейс IColumn и класс PropertyColumn являются параметризованными типами, поэтому вам просто нужно определить параметры типа для них.

Рассмотрим следующий пример:

List<Set> list = new ArrayList<Set>();

Список и ArrayList являются параметризованными типами, и их параметры типа определены. Тем не менее, Set также является параметризованным типом, но его необработанная версия используется в качестве параметра типа, поэтому компилятор выдает предупреждение в этом случае.

Мы можем исправить наш пример, явно указав все аргументы типа, например,

List<Set<Integer>> list1 = new ArrayList<Set<Integer>>();
List<Set<String>> list1 = new ArrayList<Set<String>>();

Вы должны сделать то же самое для ваших общих классов и интерфейсов.

...