1. Java / Говнокод #12583

    +79

    1. 1
    2. 2
    3. 3
    public class StringToObjectMap extends HashMap<String, Object> {
    
        public StringToObjectMap(Map<? extends String, ? extends Object> map)

    Нет слов выразить мою печаль.

    Запостил: nafania217518, 13 Февраля 2013

    Комментарии (41) RSS

    • Это как вообще?
      Ответить
    • Присоединяюсь к вопросу. Что за вопросы?
      Ответить
      • Дженерики же.

        http://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html

        Конструктор примет любой мап, у которого ключ это стринг или его потомок, а значение - любой объект. К примеру Map<String, Test>
        Ответить
        • Неожиданный синтаксис. Зато компактный.
          Ответить
          • Не пойму об чем спорить и где говно, но я бы написал так:
            public StringToObjectMap (Map<String, ?> map)
            Ибо ? extends Object==?. Это как писать class A extends Object вместо class A
            А вообще желающим постичь женерики в жабе рекомендую читать эпичный труд Анжелики Лагнер.
            Ответить
            • > эпичный труд Анжелики Лагнер
              http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
              this?
              Ответить
              • Угу. Написано понятно, с примерами и поразительной для большинства женщин ясностью и логикой.
                Ответить
            • Она ещё вроде неплохую книжку написала по плюсовым потокам
              Standard C++ IOStreams and Locales (ISBN-10: 0321585585)
              Ответить
        • А просто Map<String,Object> чё не может принять потомков?
          Ответить
          • может, но возвращать будет все равно String/Object
            Ответить
          • Сам мап в его insert'ах и прочих функциях - примет. А вот функция, принимающая Map<String, Object>, не примет Map<String, Test> (где Test extends Object).
            Ответить
            • Каст ещё никто не отменял, тем более, последствий тут никаких, это не c++.
              Ответить
              • какой каст? к чистому мапу (Map)? тогда придется дополнительно повесить @SuppressWarnings({ "rawtypes", "unchecked" })
                Ответить
                • Мне проще проиллюстрировать примером
                  http://ideone.com/YhBsrU

                  В скале этот косяк пофиксили, там ковариантность/контравариантность типа определяется на уровне класса, чтобы всем его пользователья не приходилось писать эти экзистенциальные знаки вопроса.
                  P.S. Все @SuppressWarnings локализованы.
                  Ответить
                  • > P.S. Все @SuppressWarnings локализованы.
                    Т.е. я могу писать @SuppressWarnings("НеобработанноеИсключение")?
                    Ответить
              • Последствия есть, в Derived* может сесть Base*
                Ответить
                • Ты абсолютно прав. Последствий никаких только в том случае, если мапа используется в таких методах только на чтение. Иначе нужно использовать только конкретные типы.
                  Ответить
            • Интересно, кстати.
              Чем интерфейс Map<String,Base> отличается от интерфейса Map<String,Derived>? Очевидно, что первый - подмножество второго. То есть в функцию, требующую первый, можно давать второй.

              Хотя не, в Map<String,Base> можно засунуть Base, и тогда объект при выходе из метода будет вместо Derived содержать Base, что неправильно. ШАБЛОНОПРОБЛЕМЫ.
              Вот был тупо только Map<Object,Object>, не было бы проблемы.
              Ответить
              • Мы уже обсуждали эту тему не так давно. Иммутабельные структуры + явная спецификация ковариантности/контравариантности в объявлении класса + вывод типов решают эту проблему.
                Ответить
    • здесь пожалуй только ? extends String отжег
      Ответить
      • кстати, да. String же final, и не может иметь наследников.
        Ответить
    • кстати еще метод get надо перегрузить как Object get(String)
      Ответить
    • Подождите, а что в Яве не нужно указывать параметры типа при наследовании? Я чет подзабыл...
      Ответить
      • конечно нужно
        Ответить
        • Т.е. ОП отнаследовался от генерика, создав не-генерик? Мне чет совсем проверять не хочется... но если я правильно понимаю, то совершил он этим что-то абсолютно бессмысленное...
          Ответить
          • Не вижу в этом ничего плохого. Смысл в этом есть.
            class Model { /* common model methods */ }
            class Widget extends Model { /* Widget methods */ }
            class Gadget extends Model { /* Gadget methods */ }
            
            class Dao<T extends Model> { /* common DAO methods */ }
            class WidgetDao extends Dao<Widget> { /* Specific methods for widgets manipulation */}
            class GadgetDao extends Dao<Gadget> { /* Specific methods for gadgets manipulation */}
            Ответить
            • Я не об этом, а о том, что в конкретном случае - один тип не наследуется, а второй - самый общий. И я не могу придумать даже притянутый за уши пример, когда бы созданый автором класс мог бы создать какой-то более специфицческий метод, чем тот, который можно было бы спокойно создать в классе родителе. Т.е. если бы он еще оставил бы один параметр типа для значений - тогда это могло бы иметь хоть какой-то смысл, а так - я не могу придумать.
              Ответить
              • Ну, изначальный вопрос звучал совершенно по-другому, и я отвечал на него. Код в топике абсолютно не имеет смысла, потому и попал на этот сайт.
                Ответить
                • что именно в этом коде не имеет смысла? параметр конструктора или весь код в целом?
                  Ответить
                  • Весь код в целом. StringToObjectMap не нужен. Map<String, Object> (или Map<String, ?>, если только на чтение) хватит всем.
                    Ответить
                    • 1. автор так и не ответил - перегружен ли get
                      просто в стандартном гете ожидаемый параметр - Object
                      как следствие, для Map<String, Object> можно сделать get(24) и получить null

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

                      в целом, в расширении мэпа не вижу ничего плохого
                      Ответить
                      • 1. get не перегружен. Кроме конструктора там вообще методов нет.
                        2. В дальнейшем от этого недоразумения наследуются несколько раз, делая видимость гибкой архитектуры, когда вместо одного наследника передается инстанс другого, но все в итоге кастуется к этой говномапе.
                        Ответить
                      • >всегда можно впихнуть дополнительные хелперные методы в класс, для более удобной работы. о них автор тоже умолчал
                        Верно. Я тоже так делаю. Только название громоздкое.
                        Например что-то такое:
                        SqlMap extends HashMap<String, Object>.
                        SqlMap - короткое название. Можно еще фабричные методы туда допилить.
                        SqlMap.make ("a",1, "b",2, "c",3);
                        Ответить
                      • Хелперные методы я обычно выношу в статические методы специального класса (стараясь не называть его Utils).
                        К примеру, у меня в проекте есть класс Structs, в котором есть методы
                        public static Map<String, Object> makeMap(Object... fieldsAndValues)
                        public static <K, V> Map<K, V> appendTo(Map<K, V> map, Object... fieldsAndValues)
                        public static Object[] array(Object... args)
                        Есть класс SafeGet с методами
                        public static long safeLong(String value, long defaultValue)
                        public static <K, V> long safeLong(Map<K, V> map, K key, long defaultValue)
                        /* more safe methods ...*/
                        и т.д.
                        Ответить
                        • хелперы конечно хорошо, но иногда удобнее полноценные методы.

                          Например
                          class Params extends HashMap<String, Object>

                          p = new Params();
                          ...
                          p.getInt("prop1")
                          p.getLong("prop2")
                          Ответить
                          • тогда уже


                            p.getMinimalAge();
                            p.getDefaultTimeout();
                            Ответить
                            • тогда смысла наследовать нет. сделать обычный поджик. ну и метод Map<String, Object> toHash()
                              Ответить
                              • Я вообще настороженно отношусь к наследованию, стараюсь не использовать без особой необходимости, ибо от него проблем больше, чем пользы.
                                Мне нравится идея построения дерева абстракций снизу вверх, когда несколько простых абстраций (вроде Map) используются для построения более высокоуровневой абстракции с другим интерфейсом. Каждый следующий уровень использует услуги нижнего, по возможности не раскрывая реализации.
                                Особенно подозрительно наследование от конкретных, т.е. не абстрактных классов.

                                Вот и в вашем случае, унаследовав класс Params от HashMap, вы нарушили инкапсуляцию, раскрыв реализацию. Кроме того, вы лишили себя возможности поддержания части инвариантов класса, ведь пользователи имеют возможность вызывать методы HashMap, даже если они не имеют никакого отношения к абстракции параметров (addAll? empty?).
                                Ответить
                                • в моем случае это всего лишь расширение конкретного класса для превнесения в него новых методов без цели изменить поведение. Мне нужен обычный мэп, но с дополнительными методами. Я использую его как мэп. Я хочу чтобы он был мэпом. Просто я не хочу писать крокодилов типа Map<String, Object> и работать через статические хелперы. Это конкретный класс, под конкретную задачу, с определенным способом использования. Он ни в коей мере не претендует на универсальность.

                                  П.С. Если мне потребуется изменить поведение - я определю новый класс, ничего не наследующий и спрячу мэп внутрь.
                                  Ответить

    Добавить комментарий