Übergibt 'dies' in einem Methodenaufruf, der in Java akzeptiert wird

93

Ist es eine gute / schlechte / akzeptable Praxis, das aktuelle Objekt in einem Methodenaufruf zu übergeben? Wie in:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

Ist die Leitung konkret bar.foo(this)akzeptabel?

maxf130
quelle
60
Warum sollte das nicht akzeptabel sein? Es ist normal.
Denys Séguret
3
Also ... das sind 8 Ja :) (Und ja, ich
halte es auf dem neuesten
2
Eine nicht statische anonyme Klasse übergibt diese Referenz der Superklasse automatisch, sodass dies akzeptabel ist. Die einzige Vorsicht wäre, auf Zirkelverweise zu achten.
Mehul Rathod
5
Es gibt jedoch eine Einschränkung: Sie sollten dies nicht im Konstruktor übergeben, da dies Ihr Objekt in einem inkonsistenten Zustand aussetzen würde. Menschen tun dies normalerweise, wenn sie Rückrufe (z. B. ActionListener) als anonyme innere Klassen erstellen und diese dann an ein anderes Objekt übergeben.
Tamas Rev
3
@dystroy obwohl ich damit einverstanden bin, ist es akzeptabel, was bedeutet, dass es akzeptabel ist, weil es üblich ist, ist wirklich schlechte Logik. Etwas zu tun, weil es üblich ist, kann Sie in große Schwierigkeiten bringen
Carrie Kendall

Antworten:

155

Es gibt keinen Grund, es nicht zu verwenden, es thisist die aktuelle Instanz und es ist absolut legitim, es zu verwenden. Tatsächlich gibt es oft keinen sauberen Weg, dies wegzulassen.

Also benutze es.

Da es schwer zu überzeugen ist, dass es ohne Beispiel akzeptabel ist (eine negative Antwort auf eine solche Frage ist immer leichter zu argumentieren), habe ich gerade eine der häufigsten java.langKlassen eröffnet, die Stringeine, und natürlich habe ich zum Beispiel Beispiele für diese Verwendung gefunden

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

Suchen Sie (thisin großen "akzeptierten" Projekten, Sie werden es nicht verfehlen, es zu finden.

Denys Séguret
quelle
6
Perfekte Antwort.
maxf130
15
-1, weil nicht erwähnt wird, dass bidirektionale Klassenbeziehungen komplizierter sind als unidirektionale Beziehungen. Es ist wichtig sicherzustellen, dass die Software so klar wie möglich ist. In dem obigen speziellen Beispiel wäre es sinnvoller, die Methode foo in die Baz-Klasse zu verschieben, um eine bidirektionale Referenz zwischen den beiden Klassen zu vermeiden und das Verhalten und die Daten zusammenzuführen.
JW.
35
-1 auf eine Antwort, weil Sie in der Frage ein zusätzliches kleines Detail gefunden haben, zu dem Sie einen Kommentar abgeben können? Ja wirklich ?
Denys Séguret
13
@dystroy Ja, ich bin mit Ihrem Eröffnungssatz nicht einverstanden: "Es gibt keinen Grund, ihn nicht zu verwenden".
JW.
4
In der Praxis thisbedeutet das Übergeben im realen Code (im Gegensatz zum vereinfachten Beispiel von OP) nicht, dass Sie einen bidirektionalen Link hinzufügen, beispielsweise aufgrund von Vererbung und Schnittstellen.
Denys Séguret
165

Daran ist nichts auszusetzen. Was NICHT eine gute Praxis ist, ist, dasselbe innerhalb von Konstruktoren zu tun, da Sie einen Verweis auf ein noch nicht vollständig initialisiertes Objekt geben würden.

Hier gibt es eine Art ähnlichen Beitrag: Java leckt diesen im Konstruktor, wo erklärt wird, warum letzteres eine schlechte Praxis ist.

Morgano
quelle
18
+1: Gut, um auf die Gefahr hinzuweisen, wenn in Konstruktoren auf "dies" Bezug genommen wird.
Bathseba
Es ist nicht unbedingt eine schlechte Praxis. Zum Beispiel kann ein CarKonstruktor WheelInstanzen erstellen , ein Carohne Wheelwürde unvollständig initialisiert, während ein Wheelohne ein entsprechendes Carebenfalls unvollständig initialisiert würde. In diesem Fall kann es im Konstruktor des Autos akzeptabel sein, an den Konstruktor thisdes Rades zu übergeben. Die andere Alternative wäre, sowohl Auto als auch Rad einen privaten Konstruktor zu haben und eine Werksfunktion zu verwenden, die das Auto, das Rad und das Rad am Auto installiert. Aber sollte das eine statische Methode für das Auto oder eine statische Methode für das Rad sein?
Lie Ryan
Natürlich sollten Sie eine erstellen CarFactoryWheelInstallerProxy, die die Räder für Sie installiert.
Kevin
6
@LieRyan Wheelist völlig untergeordnet Car, und IMO sollte überhaupt nichts davon wissen Car.
Izkata
3
Das EINZIGE Schlechte an der Verwendung thisinnerhalb eines Konstruktors ist, wenn thises an eine Methode oder einen Kontext übergeben wird, aus der die noch nicht vollständig konstruierte Objektreferenz an nicht vertrauenswürdige oder unbekannte Clients (oder an Clientcode, der davon ausgeht, dass sie eine Ansicht zu a hat) veröffentlicht wird vollständig konstruiertes Objekt). Der Übergang thisvon einem Konstruktor zu einer paketprivaten Methode, die eine gemeinsame Initialisierung durchführt, ist meiner Meinung nach nicht nur akzeptabel, sondern auch wünschenswert.
Scottb
42

Ja , aber Sie sollten in zwei Punkten vorsichtig sein

  1. Vorbei an diesen , wenn das Objekt noch nicht gebaut worden ist (dh in seinem Konstruktor)
  2. Wenn Sie dies an ein langlebiges Objekt weitergeben, bleibt die Referenz am Leben und es wird verhindert, dass dieses Objekt durch Müll gesammelt wird.
Stefanos T.
quelle
1
Beachten Sie, dass ein Konstruktor in Java nicht wirklich ein Konstruktor ist. Es ist wahrscheinlich besser, den Konstruktor von Java als "Initialisierer" zu bezeichnen. Innerhalb des Java-Konstruktors wurde dem Objekt tatsächlich Speicher zugewiesen, dh das Objekt war bereits im Konstruktor vorhanden / konstruiert.
Lie Ryan
1
Nicht wirklich, das Objekt verfügt möglicherweise über einige Instanzvariablen, die noch nicht initialisiert wurden, sodass das Objekt noch nicht vollständig funktionsfähig ist. Im Konstruktor können Sie dies also an ein zweites Objekt übergeben, das eine Methode für das Objekt aufruft, das noch nicht alle Instanzvariablen initialisiert hat.
Stefanos T.
Solange das zweite Objekt weiß, dass das übergebene Objekt nicht initialisiert ist und es als undurchsichtiges Objekt behandelt oder nur Methoden aufruft, die in einem solchen Zustand als sicher deklariert wurden, kann es problemlos an das Objekt übergeben werden this. Dies wäre unmöglich gewesen, wenn das Objekt nicht zugewiesen worden wäre.
Lie Ryan
13

Es ist völlig normal und vollkommen akzeptabel.

Bathseba
quelle
5

Dies steht für das aktuelle Objekt. Was Sie tun, ist sytatisch korrekt, aber ich sehe keine Notwendigkeit dafür, wenn Sie die Methode in derselben Klasse aufrufen.

Juned Ahsan
quelle
2
Im Beispielcode ist Baz.method () eine Instanzmethode, die Bar.foo () mit der Instanz von Baz als Parameter aufruft. Das OP ruft also keine Methode in derselben Klasse auf.
ein CVn
@ MichaelKjörling Ich denke, Juned sagt, dass es durch Verschieben der foo () -Methode in die Baz-Klasse nicht notwendig wäre, thiszwischen den beiden Klassen zu wechseln. Es ist also nicht erforderlich, die zusätzliche Komplexität hinzuzufügen.
JW.
4

Es wird empfohlen , das aktuelle Objekt in einem Methodenaufruf zu übergeben, wenn weniger komplexe Alternativen vorhanden sind, um dasselbe Verhalten zu erzielen.

Per Definition wird eine bidirektionale Zuordnung erstellt, sobald sie thisvon einem Objekt zu einem anderen übergeben wird.

Um Refactoring von Martin Fowler zu zitieren:

Ändern Sie die bidirektionale Zuordnung in unidirektional (200).

Bidirektionale Assoziationen sind nützlich, aber sie haben einen Preis. Der Preis ist die zusätzliche Komplexität der Aufrechterhaltung der bidirektionalen Verbindungen und der Sicherstellung, dass Objekte ordnungsgemäß erstellt und entfernt werden. Bidirektionale Assoziationen sind für viele Programmierer nicht selbstverständlich, daher sind sie häufig eine Fehlerquelle

...

Sie sollten bidirektionale Assoziationen verwenden, wenn Sie müssen, aber nicht, wenn Sie dies nicht tun. Sobald Sie sehen, dass eine bidirektionale Assoziation nicht mehr an Gewicht verliert, lassen Sie das unnötige Ende fallen.

Theoretisch sollten wir also Alarmglocken hören, wenn wir feststellen, dass wir bestehen müssen, thisund uns wirklich bemühen, andere Wege zu finden, um das vorliegende Problem zu lösen. Es gibt natürlich Zeiten, in denen es letztendlich Sinn macht, dies zu tun.

Außerdem ist es häufig erforderlich, Ihr Design vorübergehend zu beschädigen und während einer längerfristigen Umgestaltung Ihres Codes „schlechte Praktiken“ auszuführen, um eine allgemeine Verbesserung zu erzielen. (Ein Schritt zurück, zwei Schritte vorwärts).

In der Praxis habe ich festgestellt, dass sich mein Code massiv verbessert hat, indem bidirektionale Links wie die Pest vermieden wurden.

JW.
quelle
Sie verwechseln ein vereinfachtes Beispiel mit der Notwendigkeit, eine bidirektionale Verbindung herzustellen. Wenn Sie dies als Parameter übergeben, wie dies bei vielen Beispielen des java.lang-Quellcodes (zum Beispiel dem, den Sie in meiner Antwort gesehen haben) deutlich werden sollte, bedeutet dies nicht, dass Sie eine bidirektionale Abhängigkeit hinzufügen. Diese Antwort hätte meiner Meinung nach ein Kommentar sein sollen.
Denys Séguret
@dystroy Danke, dass du einen Kommentar hinzugefügt hast, um zu erklären, warum du herabgestimmt hast. Es ist immer gut zu wissen. Ich werde meine Antwort ändern, um klarzustellen, dass per Definition eine bidirektionale Zuordnung erstellt wird, sobald sie thisbestanden wird.
JW.
1
"Per Definition wird eine bidirektionale Zuordnung erstellt, sobald diese übergeben wird" . Dies macht deutlich, wo Sie nicht verstehen. Schauen Sie sich das Beispiel an, das ich gebe. Es gibt keine bidirektionale Verknüpfung, da der Typ des Arguments in equalsObject ist. Dies ist sehr häufig: Die Empfangsmethode definiert ihr Argument als allgemeinere Klasse oder als Schnittstelle. Einer der Gründe, warum ein solches Muster in Java verwendet wird , besteht darin, unerwünschte Abhängigkeiten zu vermeiden. Bevor Sie länger gehen, würde ich vorschlagen, dass Sie sich die ansehen vielen Vorkommnissethis als Argument in seriösen Java-Bibliotheken auftreten.
Denys Séguret
Lassen Sie uns einfach zustimmen, nicht zuzustimmen. Mein Leben ist viel einfacher geworden, da ich es nach thisMöglichkeit vermieden habe, meinen Code weiterzugeben. Ich würde anderen empfehlen, dies zu tun.
JW.
2
@JW: Die Argumentation in dieser Antwort ist irrelevant. Es ist immer eine schlechte Idee, X zu machen, wenn es etwas gibt, das einfacher ist, für jeden Wert von X.
Lie Ryan
4

Ja. Sie können es verwenden. Es ist nur üblich in der Programmierung zu bestehen. thisAber es gibt Vor- und Nachteile bei der Verwendung. Trotzdem ist es nicht gefährlich, dies zu tun.

Suresh Atta
quelle
Es gibt viele Nebenwirkungen. Es erhöht die Komplexität.
JW.
Wenn diese vielen Nebenwirkungen vorhanden sind, konnten wir in unserem Java-Quellcode keinen einzigen solchen Beweis finden. Siehe Beispiel @destroys aus dem Quellcode.
Suresh Atta
2

Nur um ein weiteres Beispiel hinzuzufügen, bei dem die Übergabe thiskorrekt ist und einem guten Design folgt: Besuchermuster . Im Besucherentwurfsmuster wird die Methode accept(Visitor v)normalerweise so implementiert, wie sie nur aufgerufen wird v.visit(this).

Petr Zelenka
quelle
1

Akzeptabel

Ausschnitt aus Oracle JAVA-Dokumenten:

Innerhalb einer Instanzmethode oder eines Konstruktors ist dies eine Referenz auf das aktuelle Objekt - das Objekt, dessen Methode oder Konstruktor aufgerufen wird. Mit dieser Option können Sie innerhalb einer Instanzmethode oder eines Konstruktors auf ein beliebiges Mitglied des aktuellen Objekts verweisen.

Verwenden Sie dies mit einem Feld

Der häufigste Grund für die Verwendung dieses Schlüsselworts ist, dass ein Feld von einer Methode oder einem Konstruktorparameter beschattet wird.

Nargis
quelle
2
„Sie beziehen können jedes Mitglied des aktuellen Objekts “ - das scheint nicht die Frage zu beantworten „ist es akzeptabel zu übergeben thisals Parameter? “.
ein CVn
2
Hier erfahren Sie, wie Sie this.some_variableauf die Klassenvariable anstatt auf eine lokale Variable verweisen können. Es hat nichts mit der Übergabe thisals Parameter zu tun .
Jose Salvatierra
0

Alles in Java wird als Wert übergeben. Objekte werden jedoch NIEMALS an die Methode übergeben!
Wenn Java ein Objekt an eine Methode übergibt, erstellt es zuerst eine Kopie eines Verweises auf das Objekt, nicht eine Kopie des Objekts selbst. Daher ist dies eine perfekt verwendete Methode in Java. Und am häufigsten gefolgt Verwendung.

blganesh101
quelle
9
Das sieht thematisch aus.
Denys Séguret
4
"Alles in Java wird als Wert übergeben." - Das ist ein sehr irreführender erster Kommentar. Tatsächlich werden alle Objekte als Referenz und alle primitiven Typen als Wert übergeben. Sie haben NIE einen thisVerweis auf einen primitiven Typ, und daher denke ich, dass Ihre "mehr Informationen" Verwirrung stiften.
Stewart
1
@Stewart: Er macht sofort klar, dass er nicht meint, dass ganze Objekte kopiert werden.
LarsH
10
@Stewart nein, Objekte werden nicht als Referenz übergeben, sondern Objektreferenzen werden als Wert übergeben. Es ist eine wichtige Unterscheidung - Referenzübergabe würde bedeuten, dass wenn ich eine lokale Variable habe, die auf ein Objekt verweist, und diese Variable an eine andere Methode übergeben würde, die Methode in der Lage wäre, zu ändern, auf welches Objekt sich meine Variable bezieht, und dies ist definitiv nichts Sie können in Java tun. Die Methode kann den Status des Objekts durch eine eigene Kopie der Referenz ändern, aber meine Kopie der Referenz kann nicht so geändert werden, dass sie auf etwas anderes verweist.
Ian Roberts
2
@Stewart yoda.arachsys.com/csharp/parameters.html ist ein guter Artikel, der den Unterschied zwischen dem Übergeben von Referenzen und dem Übergeben von Referenzen nach Werten im Kontext von C # erklärt, das beide unterstützt.
Ian Roberts