Il y a quelque temps, j'ai écrit cette réponse à une question sur la façon d'éviter d'avoir un getter et un setter pour chaque variable mutable. À l’époque, j’avais le sentiment difficile que j’entendais dire que c’était une mauvaise idée, mais OP demandait explicitement comment le faire. J'ai cherché ici pourquoi cela pouvait être un problème et j'ai trouvé cette question , dont les réponses semblent prendre pour acquis que l'utilisation de la réflexion à moins que cela ne soit absolument nécessaire est une mauvaise pratique.
Alors, quelqu'un peut-il expliquer pourquoi il est évident que la réflexion doit être évitée, en particulier dans le cas du getter et du setter génériques?
java
reflection
HAEM
la source
la source
Map
deget
etput
, ce qui est une façon assez maladroite de faire les choses.Réponses:
Inconvénients de la réflexion en général
La réflexion est plus difficile à comprendre que le code en ligne droite.
D'après mon expérience, la réflexion est une fonctionnalité "de niveau expert" en Java. Je dirais que la plupart des programmeurs n’utilisent jamais de réflexion de manière active (c’est-à-dire que les bibliothèques consommatrices de réflexion ne comptent pas). Cela rend le code plus difficile à comprendre pour ces programmeurs.
Le code de réflexion est inaccessible à l'analyse statique
Supposons que j'ai un getter
getFoo
dans ma classe et que je veuille le renommergetBar
. Si je n'utilise pas de réflexion, je peux simplement chercher dans la base de codegetFoo
et trouver chaque endroit qui utilise le getter afin que je puisse le mettre à jour, et même si j'en manque un, le compilateur se plaindra.Mais si le lieu qui utilise le getter est quelque chose comme
callGetter("Foo")
etcallGetter
faitgetClass().getMethod("get"+name).invoke(this)
, la méthode ci - dessus ne trouvera pas, et le compilateur ne se plaindra pas. Vous obtiendrez unNoSuchMethodException
. Et imaginez la douleur que vous éprouvez si cette exception (qui est suivie) est avaléecallGetter
car "elle est uniquement utilisée avec des chaînes codées en dur, cela ne peut pas réellement arriver". (Personne ne ferait cela, pourrait-on arguer? Sauf que l'OP l'a fait exactement dans sa réponse SO. Si le champ est renommé, les utilisateurs du configurateur générique ne le remarqueront jamais, à l'exception du bogue extrêmement obscur du poseur qui ne fait rien en silence. Les utilisateurs du getter peuvent, s’ils ont de la chance, remarquer la sortie console de l’exception ignorée.)Le code de réflexion n'est pas vérifié par le compilateur
Ceci est fondamentalement un gros sous-point de ce qui précède. Le code de réflexion est tout au sujet
Object
. Les types sont vérifiés à l'exécution. Les erreurs sont découvertes par des tests unitaires, mais uniquement si vous êtes couvert. ("C'est juste un getter, je n'ai pas besoin de le tester.") En gros, vous perdez l'avantage en utilisant Java par rapport à Python.Le code de réflexion n'est pas disponible pour l'optimisation
Peut-être pas en théorie, mais en pratique, vous ne trouverez pas de machine virtuelle qui installe ou crée un cache en ligne
Method.invoke
. Des appels de méthode normaux sont disponibles pour de telles optimisations. Cela les rend beaucoup plus rapide.Le code de réflexion est simplement lent en général
La recherche de méthode dynamique et la vérification de type nécessaires pour le code de réflexion sont plus lentes que les appels de méthode normaux. Si vous transformez ce getter bon marché sur une ligne en une bête de réflexion, vous risquez (je ne l’ai pas encore mesuré) d’observer un ralentissement de plusieurs ordres de grandeur.
Inconvénient du getter / setter générique en particulier
C'est juste une mauvaise idée, car votre classe n'a plus d'encapsulation. Chaque champ dont il dispose est accessible. Vous pourriez aussi bien les rendre tous publics.
la source
Parce que les getter / setters pass-through sont une abomination qui ne fournit aucune valeur. Ils ne fournissent aucune encapsulation car les utilisateurs peuvent obtenir les valeurs réelles via les propriétés. Ils sont YAGNI parce que vous allez rarement, voire jamais, changer la mise en œuvre de votre stockage sur le terrain.
Et parce que c'est beaucoup moins performant. En C # au moins, un getter / setter s'aligne directement sur une seule instruction CIL. Dans votre réponse, vous remplacez cela par 3 appels de fonction, qui doivent à leur tour charger des métadonnées pour les refléter. J'ai généralement utilisé 10x comme règle de base pour les coûts de réflexion, mais lorsque j'ai cherché des données, l'une des premières réponses incluait cette réponse qui lui a permis de se rapprocher de 1000x.
(Et cela rend votre code plus difficile à déboguer, et cela nuit à la convivialité, car il existe plus de moyens de le mal utiliser, et à la maintenabilité, car vous utilisez maintenant des chaînes magiques plutôt que des identifiants appropriés ...)
la source
getX()
etsetX()
méthodes qui peuvent être trouvés par réflexion. Les haricots ne sont pas nécessairement censés encapsuler leurs valeurs, ils peuvent simplement être des DTO. Ainsi, à Java, les getters sont souvent une douleur nécessaire. Les propriétés C # sont beaucoup, beaucoup plus belles à tous égards.En plus des arguments déjà présentés.
Il ne fournit pas l'interface attendue.
Les utilisateurs s'attendent à appeler foo.getBar () et foo.setBar (valeur), et non foo.get ("bar") et foo.set ("bar", valeur)
Pour fournir l’interface attendue par les utilisateurs, vous devez écrire un getter / setter séparé pour chaque propriété. Une fois que vous avez fait cela, il est inutile d'utiliser la réflexion.
la source
Bien sûr, ce n’est pas une mauvaise idée, c’est juste que dans un monde Java normal, c’est une mauvaise idée (quand vous ne voulez qu’un getter ou un setter). Par exemple, certains frameworks (spring mvc) ou certaines bibliothèques l'utilisent déjà (jstl) de cette façon, certains analyseurs json ou xml l'utilisent, si vous voulez utiliser un DSL, vous allez l'utiliser. Ensuite, cela dépend de votre scénario d'utilisation.
Rien n'est vrai ou faux comme un fait.
la source