Некоторое время назад я написал этот ответ на вопрос о том, как избежать использования методов получения и установки для каждой изменяемой переменной. В то время у меня было только трудно понять, что это плохая идея, но ОП явно спрашивал, как это сделать. Я искал здесь, почему это может быть проблемой, и нашел этот вопрос , ответы которого, по-видимому, воспринимаются как данность, что использование рефлексии, если в этом нет абсолютной необходимости, является плохой практикой.
Итак, может ли кто-то выразить словами, почему следует избегать рефлексии, особенно в случае универсального метода получения и установки?
java
reflection
гема
источник
источник
Map
«sget
иput
, что является довольно неуклюжим способом делать вещи.Ответы:
Недостатки размышлений в целом
Отражение сложнее понять, чем прямой код.
По моему опыту, отражение - это функция «экспертного уровня» в Java. Я бы сказал, что большинство программистов никогда не используют отражение активно (то есть использование библиотек, использующих отражение, не считается). Это затрудняет понимание кода для этих программистов.
Код отражения недоступен для статического анализа
Предположим, у меня
getFoo
в классе есть получатель, и я хочу его переименоватьgetBar
. Если я не использую отражения, я могу просто найти базу кодаgetFoo
и найти все места, где используется геттер, чтобы я мог обновить его, и даже если я пропущу один из них, компилятор будет жаловаться.Но если место, которое использует метод получения, является чем-то вроде
callGetter("Foo")
иcallGetter
делаетgetClass().getMethod("get"+name).invoke(this)
, то вышеупомянутый метод не найдет его, и компилятор не будет жаловаться. Только когда код действительно будет выполнен, вы получитеNoSuchMethodException
. И представьте боль, в которой вы находитесь, если это исключение (которое отслеживается) проглатывается,callGetter
потому что «оно используется только с жестко закодированными строками, на самом деле это не может произойти». (Никто не станет этого делать, кто-то может поспорить? За исключением того, что ОП сделал именно это в своем ответе SO. Если поле переименовано, пользователи универсального установщика никогда не заметят, за исключением крайне неясной ошибки установщика, молча ничего не делающей. Пользователи получателя могут, если им повезет, заметить вывод консоли игнорируемого исключения.)Код отражения не проверяется типом компилятором
Это в основном большой подпункт выше. Отражение кода это все о
Object
. Типы проверяются во время выполнения. Ошибки обнаруживаются модульными тестами, но только если у вас есть покрытие. («Это всего лишь получатель, мне не нужно его проверять».) По сути, вы теряете преимущество, используя Java над Python, которое вы получили в первую очередь.Код отражения недоступен для оптимизации
Возможно, не в теории, но на практике вы не найдете JVM, которая встроит или создаст встроенный кеш для
Method.invoke
. Для такой оптимизации доступны обычные вызовы методов. Это делает их намного быстрее.Отражение кода просто медленно
Динамический поиск методов и проверка типов, необходимые для кода отражения, выполняются медленнее, чем обычные вызовы методов. Если вы превратите этот дешевый однострочный геттер в зверя-отражателя, вы могли бы (я не измерял это) смотреть на несколько порядков замедления.
Недостаток универсального геттера / сеттера, в частности
Это просто плохая идея, потому что у вашего класса больше нет инкапсуляции. Каждое имеющееся поле доступно. Вы могли бы также сделать их всех публичными.
источник
Потому что сквозные геттеры / сеттеры - это мерзость, которая не дает никакой ценности. Они не обеспечивают инкапсуляцию, так как пользователи могут получить действительные значения через свойства. Они YAGNI, потому что вы редко, если когда-либо, будете менять реализацию своего полевого хранилища.
И потому что это гораздо менее производительно. В C #, по крайней мере, метод получения / установки будет встроен прямо в одну инструкцию CIL. В своем ответе вы заменяете это тремя вызовами функций, которые, в свою очередь, должны загружать метаданные для отражения. Я обычно использовал 10x как практическое правило для затрат на рефлексию, но когда я искал данные, один из первых ответов включал этот ответ, который приблизил его к 1000x.
(И это делает ваш код более сложным для отладки, и это вредит удобству использования, потому что есть больше способов злоупотреблять им, и это вредит удобству обслуживания, потому что вы теперь используете магические строки, а не правильные идентификаторы ...)
источник
getX()
иsetX()
методы , которые могут быть найдены с помощью отражения. Бины не обязательно должны инкапсулировать свои значения, они могут быть просто DTO. Так что в Java геттеры часто являются необходимой болью. Свойства C # намного, намного приятнее во всех отношениях.Помимо аргументов уже представлены.
Это не обеспечивает ожидаемый интерфейс.
Пользователи ожидают вызова foo.getBar () и foo.setBar (value), а не foo.get ("bar") и foo.set ("bar", value)
Чтобы предоставить интерфейс, который ожидают пользователи, вам нужно написать отдельный метод получения / установки для каждого свойства. Как только вы это сделали, нет смысла использовать рефлексию.
источник
Конечно, это не плохая идея, просто плохая идея в обычном Java-мире (когда вам нужен только геттер или сеттер). Например, некоторые фреймворки (spring mvc) или librares уже используют его (jstl) таким образом, некоторые парсеры json или xml используют его, если вы хотите использовать DSL, вы собираетесь его использовать. Тогда это зависит от вашего сценария использования.
Ничто не является правильным или неправильным как факт.
источник