Vor einiger Zeit habe ich diese Antwort auf die Frage geschrieben, wie man vermeidet, für jede veränderbare Variable einen Getter und Setter zu haben. Zu der Zeit hatte ich nur ein schwer zu verbalisierendes Gefühl, dass dies eine schlechte Idee ist, aber OP fragte ausdrücklich, wie es geht. Ich habe hier nach dem Grund gesucht, warum dies ein Problem sein könnte, und diese Frage gefunden , deren Antworten es als gegeben zu betrachten scheinen, dass die Verwendung von Reflexion, es sei denn, dies ist absolut notwendig, eine schlechte Praxis.
Kann also jemand verbalisieren, warum es selbstverständlich ist, dass Reflexionen vermieden werden sollten, insbesondere im Fall des generischen Getters und Setters?
java
reflection
HAEM
quelle
quelle
Map
'sget
und konsumierenput
, was eine ziemlich klobige Art ist, Dinge zu tun.Antworten:
Nachteile der Reflexion im Allgemeinen
Reflexion ist schwerer zu verstehen als linearer Code.
Meiner Erfahrung nach ist Reflektion in Java ein Feature auf Expertenebene. Ich würde argumentieren, dass die meisten Programmierer Reflection niemals aktiv verwenden (dh Bibliotheken, die Reflection verwenden, werden nicht gezählt). Für diese Programmierer ist die Verwendung von Code daher schwieriger zu verstehen.
Der Reflektionscode ist für die statische Analyse nicht zugänglich
Angenommen, ich habe einen Getter
getFoo
in meiner Klasse und möchte ihn in umbenennengetBar
. Wenn ich keine Reflektion verwende, kann ich einfach die Codebasis durchsuchengetFoo
und jeden Ort finden, der den Getter verwendet, damit ich ihn aktualisieren kann, und selbst wenn ich einen vermisse, wird der Compiler sich beschweren.Aber wenn der Ort, an dem der Getter verwendet wird, so ähnlich ist wie
callGetter("Foo")
undcallGetter
istgetClass().getMethod("get"+name).invoke(this)
, wird er von der obigen Methode nicht gefunden und der Compiler wird sich nicht beschweren. Nur wenn der Code tatsächlich ausgeführt wird, erhalten Sie eineNoSuchMethodException
. Und stellen Sie sich den Schmerz vor, den Sie verspüren, wenn diese Ausnahme (die nachverfolgt wird) verschluckt wird,callGetter
weil "sie nur mit hartcodierten Zeichenfolgen verwendet wird, sie kann eigentlich nicht passieren". (Niemand würde das tun, könnte jemand argumentieren? Außer dass das OP genau das in seiner SO-Antwort getan hat . Wenn das Feld umbenannt wird, würden Benutzer des generischen Setters es nie bemerken, außer dass der äußerst obskure Fehler des Setters im Stillen nichts tut. Benutzer des Getters können, wenn sie Glück haben, die Konsolenausgabe der ignorierten Ausnahme bemerken.)Der Reflektionscode wird vom Compiler nicht typgeprüft
Dies ist im Grunde ein großer Unterpunkt des oben Gesagten. Reflexionscode dreht sich alles um
Object
. Typen werden zur Laufzeit geprüft. Fehler werden durch Unit-Tests entdeckt, aber nur, wenn Sie Abdeckung haben. ("Es ist nur ein Getter, ich muss es nicht testen.") Grundsätzlich verlieren Sie den Vorteil, den Sie durch die Verwendung von Java gegenüber Python gewonnen haben.Der Reflektionscode ist für die Optimierung nicht verfügbar
Vielleicht nicht in der Theorie, aber in der Praxis werden Sie keine JVM finden, die einen Inline-Cache für Inlinespeicher erstellt
Method.invoke
. Für solche Optimierungen stehen normale Methodenaufrufe zur Verfügung. Das macht sie viel schneller.Der Reflektionscode ist im Allgemeinen nur langsam
Die für den Reflection-Code erforderliche dynamische Methodensuche und Typprüfung ist langsamer als normale Methodenaufrufe. Wenn Sie diesen billigen Einzeilen-Getter in ein Reflexionstier verwandeln, könnten Sie (ich habe dies nicht gemessen) mehrere Größenordnungen der Verlangsamung betrachten.
Nachteil der generischen Getter / Setter speziell
Das ist nur eine schlechte Idee, da Ihre Klasse jetzt keine Kapselung mehr hat. Jedes Feld ist zugänglich. Sie können sie auch alle öffentlich machen.
quelle
Weil Pass-Through-Getter / Setter ein Greuel sind, der keinen Wert liefert. Sie bieten keine Kapselung, da Benutzer über die Eigenschaften auf die tatsächlichen Werte zugreifen können. Sie sind YAGNI, weil Sie die Implementierung Ihres Feldspeichers selten oder nie ändern werden.
Und weil das viel weniger performant ist. Zumindest in C # wird ein Getter / Setter direkt in eine einzelne CIL-Anweisung eingebunden. In Ihrer Antwort ersetzen Sie dies durch 3 Funktionsaufrufe, die wiederum Metadaten laden müssen, um zu reflektieren. Ich habe im Allgemeinen 10x als Faustregel für die Reflektionskosten verwendet, aber als ich nach Daten suchte, enthielt eine der ersten Antworten diese Antwort, die näher an 1000x geparkt wurde.
(Und es erschwert das Debuggen Ihres Codes und beeinträchtigt die Benutzerfreundlichkeit, da es mehr Möglichkeiten gibt, ihn zu missbrauchen, und es beeinträchtigt die Wartbarkeit, da Sie jetzt eher magische Zeichenfolgen als richtige Bezeichner verwenden ...)
quelle
getX()
undsetX()
Methoden verfügbar machen, die über Reflection gefunden werden können. Beans sollen nicht unbedingt ihre Werte verkapseln, sie könnten nur DTOs sein. In Java sind Getter oft ein notwendiger Schmerz. C # -Eigenschaften sind in jeder Hinsicht viel, viel schöner.Zusätzlich zu den bereits vorgebrachten Argumenten.
Es bietet nicht die erwartete Schnittstelle.
Benutzer erwarten, foo.getBar () und foo.setBar (value) aufzurufen, nicht foo.get ("bar") und foo.set ("bar", value)
Um die Schnittstelle bereitzustellen, die Benutzer erwarten, müssen Sie für jede Eigenschaft einen separaten Getter / Setter schreiben. Sobald Sie dies getan haben, macht es keinen Sinn, Reflexion zu verwenden.
quelle
Natürlich ist es keine schlechte Idee, es ist einfach so, als wäre in einer normalen Java-Welt eine schlechte Idee (wenn man nur einen Getter oder Setter haben möchte). Zum Beispiel verwenden einige Frameworks (spring mvc) oder Librares es bereits (jstl) auf diese Weise, einige json o xml-Parser verwenden es, wenn Sie ein DSL verwenden möchten, werden Sie es verwenden. Dann kommt es auf Ihr Anwendungsfall-Szenario an.
Nichts ist richtig oder falsch als Tatsache.
quelle