Обобщения и шаблоны проектирования Java: не параметризация ссылки на обобщенный тип - это всегда плохо? - PullRequest
5 голосов
/ 07 сентября 2011

этот вопрос частично связан с моим последним вопросом .

У меня есть универсальный класс, представляющий коллекцию универсальных объектов:

public interface MyObject<N extends Number>{}

public interface MyCollecion<N extends Number> {
    public foo(MyObject<N> obj)
}

В моем дизайне эти коллекции объектов создаются клиентским классом (назовем его CollectionBuilder) с помощью подхода абстрактного класса:

public interface AbstractCollectionFactory {
    public abstract <N extends Number> MyCollection<MyObject<N>> createCollection(String collectionType);

}

Общие коллекции должны быть построены следующим образом:

public class CollectionBuilder{
    AbstractCollectionFactory f;
    public void buildDoubleCollection(){

         MyCollection<MyObject<Double>> c = f.<Double>createCell("MyType");
    }
    public void buildIntegerCollection(){...}
}

Хорошо. Так как здесь все хорошо. CollectionBuilder знает, какой тип конкретного бетона нужно указать (в данном случае Double), и может построить правильно. Я могу скомпилировать это без предупреждения, и все должно работать нормально.

Теперь у меня есть вопрос, связанный как с дженериками, так и с дизайном моей программы.

У меня есть другой класс в моем приложении, которому нужно использовать коллекцию, созданную CollectionBuilder (давайте назовем этот класс UserClass). Класс пользователя:

  1. Не нужно знать, к какому конкретному типу бетона относится его коллекция (MyCollection<MyObject<Double>> или MyCollection<MyObject<Integer>>).
  2. выполняет некоторые операции с этими коллекциями, вызывая некоторые методы, определенные в интерфейсе MyCollection.
  3. Я бы не хотел добавлять к нему универсальный тип.

В описанной ситуации плохая идея не параметризует универсальный класс MyCollection insied UserClass?

public UserClass{
     MyCollection a;  //warning 
     MyCollection<?> b; //no warning

     public method(MyObject o){  //warning
          a.foo(b); //compile       
     }
     public method2(MyObject<?> o){
          b.foo(o); //error do not compile
     }
}

Компилятор Java всегда протестует с предупреждением, если я не укажу универсальный параметр. Во всяком случае изнутри UserClass я не знаю конкретный параметр (и я бы не хотел его знать) и даже объявил его с "?" не позволяйте мне вызывать метод foo на MyCollection.

Так вот вопрос:

  1. не параметризация ссылки на универсальный тип - это всегда плохо?
  2. если ответ на 1 - НЕТ, это правильная ситуация, если вы этого не делаете?
  3. если ответом на 1 является ДА, как я могу использовать метод MyCollection внутри UserClass, не зная их универсальных типов?
  4. Мой плохой дизайн?

Я надеюсь, что было ясно. Я боролся с этой проблемой с незапамятных времен и понятия не имею. Пожалуйста, помогите мне.

Ответы [ 4 ]

6 голосов
/ 07 сентября 2011
  1. Согласно компилятору Java, да. И этот ответ содержит много хороших вопросов о том, почему. Лично я не согласен в некоторых ситуациях, особенно когда речь идет об использовании Class (например, если у вас есть Map<Class, Object>). В таких случаях необходимость всегда прикреплять <?> к концу «Класса» просто кажется ненужной скукой и не делает код более читабельным.

    Идея о параметризации Class в зависимости от того, с каким типом Объекта он связан, во-первых, для меня всегда была немного сомнительной. Смысл Class заключается в том, чтобы иметь общий интерфейс, который можно использовать для получения информации о любом объекте (аля отражение), независимо от его типа.

    Но я отвлекся. В соответствии с Java-компилятором и разработчиками универсальных типов, вы всегда должны параметризировать свою ссылку, даже если вы используете только <?>.

  2. Не применимо. Хотя вы всегда можете не объявлять какую-либо информацию о типе, а затем использовать @SuppressWarnings("unchecked") вместе с явным приведением, чтобы сделать компилятор счастливым.

  3. См. Богемский ответ.

  4. На самом деле недостаточно информации, чтобы сказать. Хотя я и не в курсе, я не уверен, каков будет реальный вариант использования «универсального класса, представляющего коллекцию универсальных объектов». Почему бы просто не использовать коллекции напрямую?

4 голосов
/ 07 сентября 2011

Почти всегда есть способ параметризации вашего кода. Вкратце, я бы набрал UserClass, например:

public UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

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

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

public class DoubleUserClass extends UserClass<Double> {}

Чтобы уменьшить число классов, вы можете поместить нетипизированные классы в базовый класс:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }

     public static class DoubleImpl extends UserClass<Double> {}
     public static class FloatImpl extends UserClass<Float> {}
     public static class IntegerImpl extends UserClass<Integer> {}
} 

И используйте их так: new UserClass.DoubleImpl() и т. Д.

2 голосов
/ 07 сентября 2011
 MyCollection<?> b; 

 public method2(MyObject<?> o){
      b.foo(o); 
 }

Если есть внеполосная гарантия того, что b.foo(o) безопасен, тогда мы должны заставить этот вызов работать.Введите вспомогательный метод

<N extends Number>
void foo(MyCollection<N> collection, MyObject<?> obj)
{
    @SuppressWarnings("unchecked")
    MyObject<N> obj2 = (MyObject<N>)obj;

    collection.foo(obj2);
}

MyCollection<?> b;
void method2(MyObject<?> o){
     foo(b, o); // now works
}

Приведение от MyObject<?> к MyObject<N> безопасно благодаря внеполосной гарантии.В этом смысле это проверенный актерский состав, поэтому мы вправе подавить предупреждение.

В реальных приложениях нередко мы знаем о типовых отношениях больше, чем язык может выразить;программисту не нужно извиняться, если он знает больше о своем коде, чем компилятор.

Приведение с использованием дженериков немного сложно;не совсем понятно, как работает foo(b,o).

Хорошо использовать необработанные типы MyCollection, MyObject, чтобы избежать хлопот.Игнорируйте предупреждение о том, что вы не должны использовать необработанный тип.Это чепуха.

2 голосов
/ 07 сентября 2011

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

Однако, конечно, лучше указывать генерики, где это возможно.В вашем примере вы можете добавить универсальный параметр в UserClass, как предлагает Богемский.

Что касается того, является ли ваша коллекция плохим дизайном: определяемые здесь интерфейсы / инфраструктура кажутся слишком общими.Что у MyCollection есть, чего нельзя сделать просто java.util.List<YourClass>?Действительно ли нужен интерфейс MyObject?Если вы хотите «пометить» определенный тип классов, наверняка вы можете придумать более конкретное описание того, что отличает эти объекты от любых других Object?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...