Является ли хорошей практикой использование отражения, если значительно сокращается количество стандартного кода?
По сути, существует компромисс между производительностью и, возможно, удобочитаемостью с одной стороны и абстракцией / автоматизацией / сокращением стандартного кода с другой стороны.
Изменить: Вот пример рекомендуемого использования отражения .
Чтобы привести пример, предположим, что существует абстрактный класс, Base
который имеет 10 полей и имеет 3 подкласса SubclassA
, SubclassB
и SubclassC
каждый с 10 различными полями; они все простые бобы. Проблема в том, что вы получаете две Base
ссылки на типы и хотите увидеть, имеют ли их соответствующие объекты одинаковый (под) тип и равны ли они.
В качестве решения есть необработанное решение, в котором вы сначала проверяете, равны ли типы, а затем проверяете все поля или вы можете использовать отражение и динамически определять, относятся ли они к одному типу, и выполнять итерацию по всем методам, которые начинаются с «get» (соглашение через конфигурацию), вызовите их на обоих объектах и вызовите равных по результатам.
boolean compare(Base base1, Base, base2) {
if (base1 instanceof SubclassA && base2 instanceof SubclassA) {
SubclassA subclassA1 = (SubclassA) base1;
SubclassA subclassA2 = (SubclassA) base2;
compare(subclassA1, subclassA2);
} else if (base1 instanceof SubclassB && base2 instanceof SubclassB) {
//the same
}
//boilerplate
}
boolean compare(SubclassA subA1, SubclassA subA2) {
if (!subA1.getField1().equals(subA2.getField1)) {
return false;
}
if (!subA1.getField2().equals(subA2.getField2)) {
return false;
}
//boilerplate
}
boolean compare(SubclassB subB1, SubclassB subB2) {
//boilerplate
}
//boilerplate
//alternative with reflection
boolean compare(Base base1, Base base2) {
if (!base1.getClass().isAssignableFrom(base2.getClass())) {
System.out.println("not same");
System.exit(1);
}
Method[] methods = base1.getClass().getMethods();
boolean isOk = true;
for (Method method : methods) {
final String methodName = method.getName();
if (methodName.startsWith("get")) {
Object object1 = method.invoke(base1);
Object object2 = method.invoke(base2);
if(object1 == null || object2 == null) {
continue;
}
if (!object1.equals(object2)) {
System.out.println("not equals because " + object1 + " not equal with " + object2);
isOk = false;
}
}
}
if (isOk) {
System.out.println("is OK");
}
}
источник
Ответы:
Отражение была создана для определенной цели, чтобы открыть функциональность класса , который был известен во время компиляции, похоже на то , что
dlopen
иdlsym
функции делают в C. Любое использование вне , которые должны быть в значительной степени изучены.Вам когда-нибудь приходило в голову, что сами Java-дизайнеры столкнулись с этой проблемой? Вот почему практически у каждого класса есть
equals
метод. Разные классы имеют разные определения равенства. В некоторых случаях производный объект может быть равен базовому объекту. В некоторых случаях равенство может быть определено на основе частных полей без получателей. Вы не знаетеВот почему каждый объект, который хочет пользовательского равенства, должен реализовать
equals
метод. В конце концов, вы захотите поместить объекты в набор или использовать их в качестве хеш-индекса, тогда вам придется всеequals
равно реализовать . Другие языки делают это по-другому, но Java используетequals
. Вы должны придерживаться правил вашего языка.Кроме того, «шаблонный» код, если поместить его в правильный класс, довольно сложно испортить. Отражение добавляет дополнительную сложность, что означает дополнительные шансы на ошибки. Например, в вашем методе два объекта считаются равными, если один возвращается
null
для определенного поля, а другой - нет. Что если один из ваших получателей вернет один из ваших объектов без соответствующегоequals
? Вашif (!object1.equals(object2))
провал. Кроме того, он подвержен ошибкам, так как рефлексия используется редко, поэтому программисты не так хорошо знакомы с ее ошибками.источник
Злоупотребление отражением, вероятно, зависит от используемого языка. Здесь вы используете Java. В этом случае отражение следует использовать с осторожностью, потому что зачастую это всего лишь обходной путь для плохого дизайна.
Итак, вы сравниваете разные классы, это идеальная проблема для переопределения методов . Обратите внимание, что экземпляры двух разных классов никогда не должны рассматриваться как равные. Вы можете сравнить на равенство, только если у вас есть экземпляры одного класса. См. Https://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java для примера того, как правильно реализовать сравнение на равенство.
источник
compare()
метод, предполагая, что любой метод, начинающийся с «get», является геттером и безопасен и уместен для вызова как часть операции сравнения. Это нарушение предполагаемого определения интерфейса объекта, и хотя это может быть целесообразным, оно почти всегда неверно.get
является геттером, а не пользовательским методом, возвращающим что-то?Я думаю, что у вас есть две проблемы здесь.
Динамический и статический код
Это постоянный вопрос, и ответ очень самоуверенный.
С одной стороны, ваш компилятор очень хорошо ловит все виды плохого кода. Это осуществляется посредством различных форм анализа, причем типовой анализ является распространенным. Он знает, что вы не можете использовать
Banana
объект в коде, который ожидаетCog
. Это говорит вам об ошибке компиляции.Теперь он может делать это только тогда, когда он может вывести как принятый Тип, так и данный Тип из контекста. Сколько можно сделать вывод и насколько общий вывод, во многом зависит от используемого языка. Java может вывести информацию о типе с помощью таких механизмов, как Наследование, Интерфейсы и Обобщения. Пробег меняется, некоторые другие языки предоставляют меньше механизмов, а некоторые предоставляют больше. Это все еще сводится к тому, что компилятор может знать, чтобы быть правдой.
С другой стороны, ваш компилятор не может предсказать форму внешнего кода, и иногда общий алгоритм может быть выражен для многих типов, которые не могут быть легко выражены с использованием системы типов языка. В этих случаях компилятор не всегда может знать результат заранее, и он может даже не знать, какой вопрос задать. Отражение, интерфейсы и класс Object - это способ решения этих проблем в Java. Вы должны будете обеспечить правильные проверки и обработку, но иметь такой код не вредно для здоровья.
Делать ли ваш код очень конкретным или очень общим, сводится к проблеме, которую вы пытаетесь решить. Если вы можете легко выразить это, используя систему типов, сделайте это. Пусть компилятор использует свои сильные стороны и поможет вам. Если система типов не может знать заранее (внешний код), или система типов плохо подходит для общей реализации вашего алгоритма, тогда рефлексия (и другие динамические средства) - правильный инструмент для использования.
Просто знайте, что выход за пределы системы типов вашего языка удивителен. Представьте, что вы подошли к своему другу и начали разговор на английском. Внезапно добавьте несколько слов из испанского, французского и кантонского языков, которые точно выражают ваши мысли. Контекст много расскажет вашему другу, но он также может не знать, как правильно обращаться с этими словами, что приводит к всевозможным недоразумениям. Разве лучше справляться с этими недоразумениями, чем объяснять эти идеи на английском, используя больше слов?
Пользовательское равенство
Хотя я понимаю, что Java в значительной степени опирается на
equals
метод для общего сравнения двух объектов, он не всегда подходит в данном контексте.Есть другой способ, и это тоже стандарт Java. Его называют компаратором .
То, как вы реализуете свой компаратор, будет зависеть от того, что вы сравниваете и как.
equals
реализации.источник
Я предпочитаю избегать рефлексивного программирования, насколько это возможно, потому что это
Это также намного менее производительно, чем простые вызовы методов; Раньше он был медленнее на порядок или более.
Статическая проверка кода
Любой рефлексивный код ищет классы и методы, используя строки; в исходном примере он ищет любой метод, начинающийся с «get»; он будет возвращать получатели, но также и другие методы, такие как "gettysburgAddress ()". Правила могут быть ужесточены в коде, но суть в том, что это проверка во время выполнения ; IDE и компилятор не могут помочь. В целом, мне не нравится «строго типизированный» или «примитивно одержимый» код.
Труднее рассуждать о
Рефлексивный код более многословен, чем простые вызовы методов. Больше кода = больше ошибок или, по крайней мере, больше возможностей для ошибок, больше кода для чтения, тестирования и т. Д. Меньше значит больше.
Труднее рефакторинг
Потому что код является строковым / динамическим; как только отражение появляется на сцене, вы не можете рефакторировать код со 100% -ной достоверностью, используя инструменты рефакторинга IDE, поскольку IDE не может использовать рефлексивное использование.
По сути, избегайте отражения в общем коде, если это вообще возможно; Ищите улучшенный дизайн.
источник
Злоупотребление чем-либо по определению плохо, верно? Итак, давайте избавимся от (более) на данный момент.
Вы бы назвали удивительно тяжелое внутреннее использование отражения весной плохой привычкой?
Spring приручает отражение с помощью аннотаций - как и Hibernate (и, возможно, десятки / сотни других инструментов).
Следуйте этим шаблонам, если вы используете его в своем собственном коде. Используйте аннотации, чтобы гарантировать, что IDE вашего пользователя все еще может им помочь (даже если вы являетесь единственным «пользователем» вашего кода, небрежное использование отражения, вероятно, в конечном итоге вернется, чтобы укусить вас в задницу).
Однако без учета того, как ваш код будет использоваться разработчиками, даже простейшее использование отражения, вероятно, является чрезмерным.
источник
Я думаю, что большинство из этих ответов не имеют смысла.
Да, вы, вероятно, должны написать
equals()
иhashcode()
, как отметил @KarlBielefeldt.Но для класса со многими полями это может быть утомительным примером.
Итак, это зависит .
Другая возможность: если у ваших классов действительно так много полей, что написание равных утомительно, подумайте
Map.equals()
для вашего сравнения. (илиMap.hashcode()
)например ( примечание : игнорируя нулевые проверки, вероятно, следует использовать Enums вместо ключей String, многое не показано ...)
источник