Est-ce une bonne pratique d'utiliser la réflexion si elle réduit considérablement la quantité de code passe-partout?
Fondamentalement, il existe un compromis entre les performances et peut-être la lisibilité d'un côté et l'abstraction / l'automatisation / la réduction du code passe-partout de l'autre côté.
Edit: Voici un exemple d'utilisation recommandée de la réflexion .
Pour donner un exemple, supposons qu'il existe une classe abstraite Base
qui a 10 champs et 3 sous SubclassA
- classes , SubclassB
et SubclassC
chacune avec 10 champs différents; ce sont tous de simples haricots. Le problème est que vous obtenez deux Base
références de type et que vous voulez voir si leurs objets correspondants sont du même (sous-) type et sont égaux.
En tant que solutions, il existe la solution brute dans laquelle vous vérifiez d'abord si les types sont égaux, puis vérifiez tous les champs ou vous pouvez utiliser la réflexion et voir dynamiquement s'ils sont du même type et parcourir toutes les méthodes commençant par "get" (convention sur la configuration), appelez-les sur les deux objets et appelez égaux sur les résultats.
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");
}
}
la source
Réponses:
La réflexion a été créée dans un but spécifique, pour découvrir la fonctionnalité d'une classe qui était inconnue au moment de la compilation, similaire à ce que font les fonctions
dlopen
etdlsym
en C. Toute utilisation en dehors de cela devrait être soigneusement examinée.Vous est-il déjà venu à l'esprit que les concepteurs Java eux-mêmes ont rencontré ce problème? C'est pourquoi pratiquement chaque classe a une
equals
méthode. Différentes classes ont différentes définitions de l'égalité. Dans certaines circonstances, un objet dérivé peut être égal à un objet de base. Dans certaines circonstances, l'égalité pourrait être déterminée sur la base de champs privés sans obturateurs. Tu ne sais pas.C'est pourquoi chaque objet qui souhaite une égalité personnalisée doit implémenter une
equals
méthode. Finalement, vous voudrez mettre les objets dans un ensemble, ou les utiliser comme index de hachage, alors vous devrezequals
quand même l' implémenter . D'autres langages le font différemment, mais Java l'utiliseequals
. Vous devez vous en tenir aux conventions de votre langue.De plus, le code "passe-partout", s'il est placé dans la bonne classe, est assez difficile à bousiller. La réflexion ajoute une complexité supplémentaire, ce qui signifie des chances supplémentaires de bugs. Dans votre méthode, par exemple, deux objets sont considérés comme égaux si l'un retourne
null
pour un certain champ et l'autre non. Que se passe-t-il si l'un de vos getters retourne l'un de vos objets, sans un appropriéequals
? Votreif (!object1.equals(object2))
échouera. Le fait que la réflexion soit rarement utilisée est également sujette aux bogues, de sorte que les programmeurs ne sont pas aussi familiers avec ses pièges.la source
L'utilisation excessive de la réflexion dépend probablement de la langue utilisée. Ici, vous utilisez Java. Dans ce cas, la réflexion doit être utilisée avec précaution car souvent, ce n'est qu'une solution de contournement pour une mauvaise conception.
Donc, vous comparez différentes classes, c'est un problème parfait pour remplacer la méthode . Notez que les instances de deux classes différentes ne doivent jamais être considérées comme égales. Vous ne pouvez comparer l'égalité que si vous avez des instances de la même classe. Voir /programming/27581/overriding-equals-and-hashcode-in-java pour un exemple de mise en œuvre correcte de la comparaison d'égalité.
la source
compare()
méthode en supposant que toute méthode commençant par "get" est un getter et est sûre et appropriée à appeler dans le cadre d'une opération de comparaison. C'est une violation de la définition d'interface prévue de l'objet, et bien que cela puisse être utile, c'est presque toujours faux.get
est un getter et non une méthode personnalisée renvoyant quelque chose?Je pense que vous avez deux problèmes ici.
Code dynamique vs code statique
Il s'agit d'une question permanente et la réponse est très avisée.
D'une part, votre compilateur est très bon pour intercepter toutes sortes de mauvais code. Il le fait à travers diverses formes d'analyse, l'analyse de type étant courante. Il sait que vous ne pouvez pas utiliser un
Banana
objet dans le code qui attend unCog
. Il vous le dit par une erreur de compilation.Maintenant, il ne peut le faire que lorsqu'il peut déduire à la fois le type accepté et le type donné à partir du contexte. La quantité qui peut être déduite et le degré de généralité de cette inférence dépendent en grande partie de la langue utilisée. Java peut déduire des informations de type via des mécanismes tels que l'héritage, les interfaces et les génériques. Le kilométrage varie, certaines autres langues offrent moins de mécanismes et d'autres plus. Cela revient toujours à ce que le compilateur peut savoir être vrai.
D'un autre côté, votre compilateur ne peut pas prédire la forme du code étranger, et parfois un algorithme général peut être exprimé sur de nombreux types qui ne peuvent pas être facilement exprimés en utilisant le système de type du langage. Dans ces cas, le compilateur ne peut pas toujours connaître le résultat à l'avance et il peut même ne pas savoir quelle question poser. La réflexion, les interfaces et la classe Object sont la façon dont Java gère ces problèmes. Vous devrez fournir les vérifications et la manipulation correctes, mais ce n'est pas malsain d'avoir ce type de code.
Que ce soit pour rendre votre code très spécifique ou très général se résume au problème que vous essayez de gérer. Si vous pouvez facilement l'exprimer à l'aide du système de saisie, faites-le. Laissez le compilateur jouer à ses forces et vous aider. Si le système de type ne pouvait pas savoir à l'avance (code étranger), ou si le système de type est mal adapté à une implémentation générale de votre algorithme, la réflexion (et d'autres moyens dynamiques) sont le bon outil à utiliser.
Sachez simplement que sortir du système de typage de votre langue est surprenant. Imaginez vous approcher de votre ami et entamer une conversation en anglais. Laissez tomber soudainement quelques mots d'espagnol, de français et de cantonais qui expriment précisément vos pensées. Le contexte en dira beaucoup à votre ami, mais il peut également ne pas savoir comment gérer ces mots, ce qui conduit à toutes sortes de malentendus. Est-il préférable de traiter ces malentendus que d'expliquer ces idées en anglais en utilisant plus de mots?
Égalité personnalisée
Bien que je comprenne que Java s'appuie fortement sur la
equals
méthode pour comparer généralement deux objets, ce n'est pas toujours approprié dans un contexte donné.Il y a une autre manière, et c'est aussi une norme Java. Son appelé un comparateur .
Quant à la façon dont vous implémentez votre comparateur dépendra de ce que vous comparez, et comment.
equals
implémentation spécifique .la source
Je préfère éviter autant que possible la programmation réfléchissante car elle
Il est également beaucoup moins performant que les appels de méthode simples; il était auparavant plus lent d'un ordre de grandeur ou plus.
Vérification statique du code
Tout code réfléchissant recherche des classes et des méthodes à l'aide de chaînes; dans l'exemple d'origine, il recherche toute méthode commençant par "get"; il renverra des getters mais d'autres méthodes en plus, comme "gettysburgAddress ()". Les règles pourraient être resserrées dans le code, mais le fait demeure qu'il s'agit d'une vérification d'exécution ; un IDE et le compilateur ne peuvent pas aider. En général, je n'aime pas le code "typé à la chaîne" ou "obsédé par les primitifs".
Plus difficile à raisonner
Le code réfléchissant est plus détaillé que les appels de méthode simples. Plus de code = plus de bogues, ou au moins plus de potentiel pour les bogues, plus de code à lire, à tester, etc. Moins c'est plus.
Plus difficile à refactoriser
Parce que le code est basé sur une chaîne / dynamiquement; dès que la réflexion est sur la scène, vous ne pouvez pas refactoriser le code avec 100% de confiance en utilisant les outils de refactorisation d'un IDE car l'IDE ne peut pas détecter les utilisations réfléchissantes.
Fondamentalement, évitez autant que possible la réflexion dans le code général; recherchez une conception améliorée.
la source
La surutilisation de quoi que ce soit par définition est mauvaise, non? Alors débarrassons-nous du (terminé) pour l'instant.
Diriez-vous que l'utilisation interne étonnamment lourde de la réflexion au printemps est une mauvaise habitude?
Spring apprivoise la réflexion en utilisant des annotations - ainsi que Hibernate (et probablement des dizaines / centaines d'autres outils).
Suivez ces modèles si vous l'utilisez dans votre propre code. Utilisez des annotations pour vous assurer que l'EDI de votre utilisateur peut toujours l'aider (même si vous êtes le seul "utilisateur" de votre code, une utilisation imprudente de la réflexion reviendra probablement pour vous mordre dans le cul finalement).
Cependant, sans tenir compte de la façon dont votre code sera utilisé par les développeurs, même l'utilisation la plus simple de la réflexion est probablement une utilisation excessive.
la source
Je pense que la plupart de ces réponses ne sont pas pertinentes.
Oui, vous devriez probablement écrire
equals()
ethashcode()
, comme l'a noté @KarlBielefeldt.Mais, pour une classe avec de nombreux domaines, cela peut être fastidieux.
Donc ça dépend .
Une autre possibilité: si vos classes ont vraiment tellement de champs que l'écriture d'un égal est fastidieuse, envisagez
Map.equals()
pour votre comparaison. (ouMap.hashcode()
)par exemple ( note : ignorer les vérifications nulles, devrait probablement utiliser des énumérations au lieu des clés de chaîne, ce qui n'est pas souvent montré ...)
la source