Java Generics and Design Patterns: не параметризация ссылки на общий тип всегда плохо?
-
27-10-2019 - |
Вопрос
Этот вопрос частично связан с моим последним вопрос.
У меня есть общий класс, представляющий коллекцию общих объектов:
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 знает, что такое общий бетонный тип для указания (в этом случае в два раза) и может построить правильно. Я могу скомпилировать это без предупреждения, и все должно работать нормально.
Теперь у меня есть вопрос, связанный как с дженериками, так и с дизайном моей программы.
У меня есть еще один класс в моем приложении, который должен использовать коллекцию, созданную CollectionBuilder (давайте назовем этот класс userclass). Userclass:
- Не нужно знать, какой конкретный конкретный тип является его коллекцией (
MyCollection<MyObject<Double>>
или жеMyCollection<MyObject<Integer>>
). - Выполните некоторые операции по этим коллекциям, ссылающиеся на некоторые методы, определенные в интерфейсе MyCollection.
- Я не хотел бы добавлять в него общий тип.
В описанной ситуации, что плохая идея не параметризует общий класс Mycollection Insive 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 я не знаю конкретный параметр (и я хотел бы не знать его) и даже объявить его "?" Не позволяйте мне позвонить в Method Foo на MyCollection.
Итак, вопрос:
- Не параметризация ссылки на общий тип всегда плохо?
- Если ответ в 1 нет, это правильная ситуация для этого не делать?
- Если ответ в 1 - это да, как я могу использовать метод MyCollection изнутри userClass, не зная их общих типов?
- У меня плохой дизайн?
Я надеюсь, что будет ясен. Я борюсь за эту проблему с дней, и понятия не имею. Помогите мне, пожалуйста.
Решение
По словам Java Compiler, да. А также этот ответ Содержит много хороших моментов, почему. Лично я не согласен в некоторых ситуациях, особенно в случае использования
Class
обеспокоен (например, если у вас естьMap<Class, Object>
) В таких случаях приходится всегда приступать<?>
В конце «класса» просто ощущается как ненужная уточка и не делает код еще более читабельным.Идея иметь
Class
быть параметризованным на основе того, какой тип объекта он связан, всегда чувствовал себя немного сомнительным для меня. ТочкаClass
состоит в том, чтобы иметь общий интерфейс, который можно использовать для получения информации о любом объекте (отражение ALA), независимо от его типа.Но я отступаю. Согласно Java Compiler и The Ineultors of Generic Types, вы всегда должны параметризовать свою ссылку, даже если вы используете только
<?>
.Непригодный. Хотя вы всегда не можете объявить информацию о какой -либо типах, а затем использовать
@SuppressWarnings("unchecked")
наряду с явным составом, чтобы сделать компилятор счастливым.Смотрите ответ богемя.
На самом деле не хватает информации, чтобы сказать. Несмотря на то, что я не в макушке, я не уверен, какой будет фактический вариант использования для «общего класса, представляющего коллекцию общих объектов». Почему бы просто не использовать коллекции напрямую?
Другие советы
Почти всегда есть способ параметризировать ваш код. Вкратце, я буду набирать userclass, как это:
public UserClass <N extends Number> {
MyCollection<N> c;
public method(MyObject<N> o){
c.foo(o);
}
}
Редактировать
Если вы беспокоитесь о том, чтобы полюбить свой клиент-код с помощью Generics, вы можете сделать абстрактный класс Typed и предоставить непредубированные реализации, например, это:
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()
так далее
Легко увлечься дженериками. Вы должны где -то остановиться. Так что нет, я не думаю, что не параметризует ссылку на общий тип, всегда плохо. Иногда дополнительные усилия, необходимые для того, чтобы все дженерики точно не стоят.
Однако, конечно, лучше указать общие, где это возможно. В вашем примере вы можете добавить общий параметр в userclass, как предполагает богемян.
Что касается того, является ли ваша коллекция плохим дизайном: интерфейсы/инфраструктура, которую вы определяете здесь, кажется чрезмерно общей. Что значит MyCollection
это нельзя сделать просто с java.util.List<YourClass>
? Действительно ли необходимость в MyObject
интерфейс? Если вы хотите «пометить» определенный тип классов, наверняка вы можете придумать более конкретное описание того, что отличает эти объекты от любых других Object
?
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
Чтобы избежать хлопот. Не обращайте внимания на предупреждение о том, что вы не должны использовать необработанный тип. Это чепуха.