Ist es eine gute Praxis, Reflektion zu verwenden, wenn die Anzahl der Kesselschild-Codes stark reduziert wird?
Grundsätzlich gibt es einen Kompromiss zwischen Leistung und Lesbarkeit auf der einen Seite und Abstraktion / Automatisierung / Reduzierung von Code auf der anderen Seite.
Bearbeiten: Hier ist ein Beispiel für eine empfohlene Verwendung der Reflexion .
Um ein Beispiel zu geben : Angenommen , es gibt eine eine abstrakte Klasse , Base
die 10 Felder hat und hat drei Unterklassen SubclassA
, SubclassB
und SubclassC
jeweils mit 10 verschiedenen Bereichen; es sind alles einfache Bohnen. Das Problem ist, dass Sie zwei Base
Typreferenzen erhalten und sehen möchten, ob die entsprechenden Objekte vom gleichen (Unter-) Typ und gleich sind.
Als Lösungen gibt es die unformatierte Lösung, bei der Sie zuerst prüfen, ob die Typen gleich sind, und dann alle Felder prüfen, oder Sie können Reflection verwenden und dynamisch feststellen, ob sie vom gleichen Typ sind, und alle Methoden durchlaufen, die mit "get" (Konvention) beginnen über Konfiguration), rufen Sie sie für beide Objekte auf und rufen Sie gleich für die Ergebnisse auf.
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");
}
}
quelle
Antworten:
Reflection wurde für einen bestimmten Zweck erstellt, um die Funktionalität einer Klasse zu ermitteln, die zum Kompilierungszeitpunkt unbekannt war, ähnlich wie die Funktionen
dlopen
unddlsym
in C. Jede Verwendung außerhalb dieser Funktion sollte einer eingehenden Prüfung unterzogen werden.Ist Ihnen jemals aufgefallen, dass die Java-Designer selbst auf dieses Problem gestoßen sind? Deshalb hat praktisch jede Klasse eine
equals
Methode. Unterschiedliche Klassen haben unterschiedliche Definitionen von Gleichheit. Unter bestimmten Umständen kann ein abgeleitetes Objekt einem Basisobjekt entsprechen. Unter bestimmten Umständen könnte die Gleichstellung auf der Grundlage privater Felder ohne Getter bestimmt werden. Sie wissen es nicht.Deshalb sollte jedes Objekt, das eine benutzerdefinierte Gleichheit wünscht, eine
equals
Methode implementieren . Irgendwann möchten Sie die Objekte in eine Gruppe einfügen oder als Hash-Index verwenden, dann müssen Sie sieequals
trotzdem implementieren . Andere Sprachen machen es anders, aber Java verwendetequals
. Sie sollten sich an die Konventionen Ihrer Sprache halten.Außerdem ist der Code "boilerplate", wenn er in die richtige Klasse eingeordnet wird, ziemlich schwer zu verfälschen. Reflexion fügt zusätzliche Komplexität hinzu, was zusätzliche Möglichkeiten für Fehler bedeutet. In Ihrer Methode werden beispielsweise zwei Objekte als gleich betrachtet, wenn eines
null
für ein bestimmtes Feld zurückgegeben wird und das andere nicht. Was ist, wenn einer Ihrer Getter eines Ihrer Objekte ohne ein geeignetes zurückgibtequals
? Duif (!object1.equals(object2))
wirst scheitern. Außerdem ist es fehleranfällig, dass Reflexion nur selten verwendet wird, sodass Programmierer mit seinen Fallstricken nicht so vertraut sind.quelle
Übermäßiger Gebrauch von Reflexion hängt wahrscheinlich von der verwendeten Sprache ab. Hier verwenden Sie Java. In diesem Fall sollte Reflexion mit Vorsicht angewendet werden, da dies häufig nur eine Problemumgehung für schlechtes Design ist.
Wenn Sie also verschiedene Klassen vergleichen, ist dies ein perfektes Problem für das Überschreiben von Methoden . Beachten Sie, dass Instanzen von zwei verschiedenen Klassen niemals als gleich angesehen werden sollten. Sie können nur dann einen Vergleich auf Gleichheit durchführen, wenn Sie Instanzen derselben Klasse haben. Unter /programming/27581/overriding-equals-and-hashcode-in-java finden Sie ein Beispiel für die korrekte Implementierung des Gleichheitsvergleichs.
quelle
compare()
Methode führt, bei denen angenommen wird, dass jede Methode, die mit "get" beginnt, ein Getter ist und als Teil einer Vergleichsoperation sicher und angemessen aufgerufen werden kann. Es ist eine Verletzung der beabsichtigten Schnittstellendefinition des Objekts, und obwohl dies zweckmäßig sein mag, ist es fast immer falsch.get
einem Getter beginnt, keine benutzerdefinierte Methode ist, die etwas zurückgibt?Ich denke, Sie haben hier zwei Probleme.
Dynamischer vs statischer Code
Dies ist eine mehrjährige Frage und die Antwort ist sehr eigenwillig.
Einerseits ist Ihr Compiler sehr gut darin, alle Arten von schlechtem Code abzufangen. Dies geschieht durch verschiedene Formen der Analyse, wobei die Typanalyse eine übliche ist. Es ist bekannt, dass Sie
Banana
in Code, der a erwartet, kein Objekt verwenden könnenCog
. Dies wird durch einen Kompilierungsfehler angezeigt.Dies ist jetzt nur möglich, wenn sowohl der akzeptierte Typ als auch der angegebene Typ aus dem Kontext abgeleitet werden können. Wie viel gefolgert werden kann und wie allgemein diese Schlussfolgerung ist, hängt weitgehend von der verwendeten Sprache ab. Java kann Typinformationen über Mechanismen wie Vererbung, Schnittstellen und Generika ableiten. Die Laufleistung variiert, einige andere Sprachen bieten weniger Mechanismen und einige mehr. Es läuft immer noch auf das hinaus, was der Compiler als wahr erkennen kann.
Andererseits kann Ihr Compiler die Form des Fremdcodes nicht vorhersagen, und manchmal kann ein allgemeiner Algorithmus über viele Typen ausgedrückt werden, die mit dem Typensystem der Sprache nicht einfach ausgedrückt werden können. In diesen Fällen kann der Compiler das Ergebnis nicht immer im Voraus kennen, und er kann möglicherweise nicht einmal wissen, welche Frage zu stellen ist. Reflection, Interfaces und die Object-Klasse sind Javas Methode, um diese Probleme zu lösen. Sie müssen die korrekten Überprüfungen und die korrekte Behandlung bereitstellen, aber es ist nicht ungesund, diese Art von Code zu haben.
Ob Ihr Code sehr spezifisch oder sehr allgemein sein soll, hängt von dem Problem ab, mit dem Sie sich befassen möchten. Wenn Sie es mit dem Typensystem leicht ausdrücken können, tun Sie dies. Lassen Sie den Compiler seine Stärken ausspielen und helfen Sie. Wenn das Typensystem möglicherweise nicht im Voraus weiß (Fremdcode) oder das Typensystem für eine allgemeine Implementierung Ihres Algorithmus schlecht geeignet ist, sind Reflexion (und andere dynamische Mittel) das richtige Werkzeug.
Denken Sie nur daran, dass es überraschend ist, aus dem Typensystem Ihrer Sprache herauszukommen. Stellen Sie sich vor, Sie gehen auf Ihren Freund zu und beginnen ein Gespräch auf Englisch. Lassen Sie plötzlich ein paar Wörter aus dem Spanischen, Französischen und Kantonesischen fallen, um Ihre Gedanken genau auszudrücken. Der Kontext wird Ihren Freunden viel erzählen, aber sie wissen möglicherweise auch nicht genau, wie sie mit diesen Worten umgehen sollen, die zu Missverständnissen aller Art führen. Ist es besser , mit diesen Missverständnissen umzugehen, als diese Ideen auf Englisch mit mehr Worten zu erklären?
Benutzerdefinierte Gleichstellung
Ich verstehe zwar, dass Java stark von der
equals
Methode zum allgemeinen Vergleichen zweier Objekte abhängt, aber in einem bestimmten Kontext nicht immer geeignet ist.Es gibt einen anderen Weg und es ist auch ein Java-Standard. Man nennt es einen Komparator .
Wie Sie Ihren Komparator implementieren, hängt davon ab, was und wie Sie vergleichen.
equals
Implementierung.quelle
Ich bevorzuge es, reflektierende Programmierung so weit wie möglich zu vermeiden
Es ist auch viel weniger performant als einfache Methodenaufrufe; Früher war es um eine Größenordnung oder mehr langsamer.
Code statisch prüfen
Jeder reflektierende Code sucht mithilfe von Zeichenfolgen nach Klassen und Methoden. Im ursprünglichen Beispiel wird nach einer Methode gesucht, die mit "get" beginnt. Es wird Geter zurückgeben, aber auch andere Methoden, wie "gettysburgAddress ()". Die Regeln könnten im Code verschärft werden, aber der Punkt bleibt, dass es sich um eine Laufzeitprüfung handelt . Eine IDE und der Compiler können nicht helfen. Im Allgemeinen mag ich keinen "stringly typed" oder "primitive obsessed" Code.
Schwieriger zu überlegen
Reflektierender Code ist ausführlicher als einfache Methodenaufrufe. Mehr Code = mehr Fehler oder zumindest mehr Potenzial für Fehler, mehr Code zum Lesen, Testen usw. Weniger ist mehr.
Schwieriger zu refaktorieren
Weil der Code string / dynamisch basiert; Sobald die Reflektion vor Ort ist, können Sie den Code mit den Refactoring-Tools einer IDE nicht hundertprozentig umgestalten, da die IDE die reflektierenden Verwendungen nicht aufgreifen kann.
Vermeiden Sie grundsätzlich Reflexionen im allgemeinen Code, wenn dies möglich ist. Suchen Sie nach einem verbesserten Design.
quelle
Überbeanspruchung ist per definitionem schlecht, oder? Also lassen Sie uns die (Über) loswerden.
Würdest du den erstaunlich starken internen Gebrauch von Reflexion im Frühling als schlechte Angewohnheit bezeichnen?
Spring zähmt die Reflexion mithilfe von Anmerkungen - ebenso wie Hibernate (und wahrscheinlich Dutzende / Hunderte anderer Tools).
Folgen Sie diesen Mustern, wenn Sie sie in Ihrem eigenen Code verwenden. Verwenden Sie Anmerkungen, um sicherzustellen, dass die IDE Ihres Benutzers ihnen weiterhin helfen kann. (Auch wenn Sie der einzige "Benutzer" Ihres Codes sind, wird die unachtsame Verwendung der Reflexion Sie wahrscheinlich irgendwann wieder in den Hintern beißen.)
Ohne Rücksicht darauf, wie Ihr Code von den Entwicklern verwendet wird, ist jedoch die einfachste Verwendung von Reflection wahrscheinlich zu häufig.
quelle
Ich denke, die meisten dieser Antworten verfehlen den Punkt.
Ja, du solltest wohl schreiben
equals()
undhashcode()
, wie von @KarlBielefeldt vermerkt.Aber für eine Klasse mit vielen Feldern kann dies ein mühsamer Boiler sein.
Also, es hängt davon ab .
Eine andere Möglichkeit: Wenn Ihre Klassen wirklich so viele Felder haben, dass das Schreiben eines Gleichheitszeichens mühsam ist, ziehen Sie in Betracht
Map.equals()
für Ihren Vergleich. (oderMap.hashcode()
)zB ( Hinweis : Ignorieren von Null-Checks, sollte wahrscheinlich Enums anstelle von String-Schlüsseln verwenden, viel nicht gezeigt ...)
quelle