Sta passando 'this' in una chiamata al metodo accettata come pratica in java


93

È buona / cattiva / accettabile passare l'oggetto corrente in una chiamata al metodo. Come 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);
    }
}

Nello specifico, la linea è bar.foo(this)accettabile?


60
Perché non sarebbe accettabile? È comune.
Denys Séguret

3
Quindi ... sono 8 sì :) (E sì, lo
tengo

2
la classe anonima non statica passa automaticamente questo riferimento della super classe, quindi sarà accettabile. l'unica precauzione sarebbe fare attenzione ai riferimenti circolari.
Mehul Rathod

5
C'è un avvertimento però: non dovresti passare questo nel costruttore perché questo esporrebbe il tuo oggetto in uno stato incoerente. Le persone in genere lo fanno quando creano callback (ad esempio ActionListener) come classi interne anonime e poi lo passano a un altro oggetto.
Tamas Rev

3
@dystroy anche se sono d'accordo che sia accettabile, implicare che sia accettabile perché è comune è davvero una cattiva logica. Fare qualcosa perché è comune può metterti nei guai
Carrie Kendall

Risposte:


155

Non c'è motivo per non usarlo, thisè l'istanza corrente ed è perfettamente legittimo da usare. In effetti, spesso non esiste un modo pulito per ometterlo.

Quindi usalo.

Poiché è difficile convincere che sia accettabile senza esempio (una risposta negativa a una domanda del genere è sempre più facile da discutere), ho appena aperto una delle java.langclassi più comuni , Stringquella, e ovviamente ho trovato esempi di questo utilizzo, ad esempio

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

Cerca (thisnei grandi progetti "accettati", non mancherai di trovarlo.


6
Risposta perfetta.
maxf130

15
-1 perché non si fa menzione del fatto che le relazioni di classe bidirezionali sono più complicate delle relazioni unidirezionali. È fondamentale garantire che il software sia il più chiaro possibile. Nell'esempio specifico sopra sarebbe più sensato spostare il metodo foo sulla classe Baz per evitare di avere un riferimento bidirezionale tra le due classi e per riunire il comportamento ei dati.
JW.

35
-1 ad una risposta perché hai trovato nella domanda un ulteriore piccolo dettaglio su cui puoi commentare? Veramente ?
Denys Séguret

13
@dystroy Sì, non sono d'accordo con la tua frase di apertura: "Non c'è motivo per non usarlo".
JW.

4
In pratica, nel codice reale (contrariamente all'esempio semplificato di OP), passare thisnon significa aggiungere un collegamento bidirezionale, per esempio per ereditarietà e interfacce.
Denys Séguret

165

Non c'è niente di sbagliato in questo. Ciò che NON è una buona pratica è fare lo stesso all'interno dei costruttori, perché daresti un riferimento a un oggetto non ancora completamente inizializzato.

C'è una sorta di post simile qui: Java che trapela questo nel costruttore dove danno una spiegazione del perché quest'ultima è una cattiva pratica.


18
+1: buono per sottolineare il pericolo nel riferirsi a "questo" nei costruttori.
Bathsheba

Non è necessariamente una cattiva pratica. Ad esempio, un Carcostruttore può creare Wheelistanze, a Carsenza Wheelsarebbe inizializzato in modo incompleto, mentre a Wheelsenza un corrispondente Carsarebbe anche inizializzato in modo incompleto. In questo caso, può essere accettabile che il costruttore dell'auto passi thisal costruttore della ruota. L'altra alternativa sarebbe fare in modo che sia l'Auto che la Ruota abbiano un costruttore privato e utilizzino una funzione di fabbrica che costruisca l'Auto, la Ruota e installi la Ruota sull'Auto; ma dovrebbe essere un metodo statico su Car o un metodo statico su Wheel?
Lie Ryan

Ovviamente, dovresti creare uno CarFactoryWheelInstallerProxyche installi le ruote per te.
Kevin

6
@LieRyan Wheelè completamente subordinato a Car, e IMO non dovrebbe saperlo Caraffatto.
Izkata

3
L'UNICA cosa negativa dell'utilizzo thisdall'interno di un costruttore è se thisviene passato in un metodo o contesto da cui il riferimento all'oggetto non ancora completamente costruito viene pubblicato su client non attendibili o sconosciuti (o codice client che presume di avere una vista su un oggetto completamente costruito). Il passaggio thisda un costruttore a un metodo privato del pacchetto che esegue un'inizializzazione comune è, a mio parere, non solo accettabile ma desiderabile.
scottb

42

, ma dovresti stare attento a due cose

  1. Passando questo quando l'oggetto non è stato ancora costruito (cioè nel suo costruttore)
  2. Passando questo a un oggetto di lunga durata, ciò manterrà vivo il riferimento e impedirà a questo oggetto di essere spazzato via.

1
Notare che un costruttore in Java non è realmente un costruttore, probabilmente è più appropriato chiamare "inizializzatore" del costruttore di Java. All'interno del costruttore Java, all'oggetto è stata effettivamente allocata memoria, questo l'oggetto in realtà è già esistito / costruito all'interno del costruttore.
Lie Ryan

1
Non proprio, l'oggetto potrebbe avere alcune variabili di istanza che non sono state ancora inizializzate, quindi l'oggetto non è ancora completamente operativo. Quindi, nel costruttore, potresti passarlo a un secondo oggetto, che potrebbe invocare un metodo per l'oggetto che non ha ancora inizializzato tutte le sue variabili di istanza.
Stefanos T.

tuttavia, finché il secondo oggetto è consapevole che l'oggetto passato non è inizializzato e lo tratta come un oggetto opaco, o chiama solo metodi che sono stati dichiarati sicuri in tale stato, non causerà alcun problema a passargli this. Farlo sarebbe stato impossibile se l'oggetto non fosse stato assegnato.
Lie Ryan


5

questo sta per l'oggetto corrente. Quello che stai facendo è sistematicamente corretto, ma non vedo la necessità di questo se stai chiamando il metodo nella stessa classe.


2
Nel codice di esempio, Baz.method () è un metodo di istanza che chiama Bar.foo () con l'istanza di Baz come parametro. Quindi l'OP non chiama un metodo nella stessa classe.
un CVn

@ MichaelKjörling Penso che Juned stia dicendo che, spostando il metodo foo () nella classe Baz, non ci sarebbe bisogno di passare thistra le due classi. Quindi, non è necessario aggiungere ulteriore complessità.
JW.

4

È una cattiva pratica passare l'oggetto corrente in una chiamata al metodo se esistono alternative meno complesse per ottenere lo stesso comportamento.

Per definizione, un'associazione bidirezionale viene creata non appena thissi passa da un oggetto all'altro.

Per citare Refactoring, di Martin Fowler:

Cambia associazione bidirezionale in unidirezionale (200)

Le associazioni bidirezionali sono utili, ma hanno un prezzo. Il prezzo è l'ulteriore complessità del mantenimento dei collegamenti a due vie e la garanzia che gli oggetti vengano creati e rimossi correttamente. Le associazioni bidirezionali non sono naturali per molti programmatori, quindi spesso sono fonte di errori

...

Dovresti usare associazioni bidirezionali quando ne hai bisogno, ma non quando non lo fai. Non appena vedi che un'associazione bidirezionale non sta più tirando il suo peso, lascia cadere l'estremità non necessaria.

Quindi, in teoria, dovremmo sentire un campanello d'allarme quando scopriamo di dover passare thise sforzarci di pensare ad altri modi per risolvere il problema in questione. Ci sono, naturalmente, momenti in cui, in ultima istanza, ha senso farlo.

Inoltre è spesso necessario corrompere temporaneamente il progetto, facendo "cattive pratiche", durante un refactoring a lungo termine del codice per un miglioramento generale. (Un passo indietro, due passi avanti).

In pratica ho trovato il mio codice è migliorata in maniera massiccia , evitando i collegamenti bidirezionali come la peste.


Confondi un esempio semplificato con la necessità di stabilire un collegamento bidirezionale. Passare questo come parametro, come dovrebbe essere chiaro con molti esempi del codice sorgente java.lang (ad esempio quello che hai visto nella mia risposta) non significa che aggiungi una dipendenza bidirezionale. Questa risposta avrebbe dovuto essere un commento secondo me.
Denys Séguret

@dystroy Grazie per aver aggiunto un commento per spiegare perché hai svalutato. È sempre bello saperlo. Modificherò la mia risposta per chiarire che, per definizione, un'associazione bidirezionale viene creata non appena thisviene approvata.
JW.

1
"per definizione, viene creata un'associazione bidirezionale non appena questa viene superata" . Questo rende chiaro dove non riesci a capire. Guarda l'esempio che do. Non è presente alcun collegamento bidirezionale perché il tipo di argomento in equalsè Object. Questo è molto comune: il metodo ricevente definisce il suo argomento come una classe più generale o come un'interfaccia. Uno dei motivi per cui un tale modello viene utilizzato in java è per evitare dipendenze indesiderate. Prima di proseguire ti suggerisco di dare un'occhiata alle numerose occorrenze di passaggio thiscome argomento nelle rispettabili librerie java.
Denys Séguret

Accettiamo solo di non essere d'accordo. La mia vita è diventata molto più facile da quando ho evitato di passare il thismio codice, ove possibile. Consiglierei ad altri di farlo.
JW.

2
@JW: il ragionamento fornito in questa risposta è irrilevante. È sempre una cattiva idea fare X se c'è qualcos'altro che è più semplice, per qualsiasi valore di X.
Lie Ryan

4

Sì. Puoi usarlo È comune nella programmazione passare, ma thisci sono pro e contro nell'usarlo, ma non è pericoloso farlo.


Ci sono molti effetti collaterali. Aggiunge complessità.
JW.

Se ci sono questi molti effetti collaterali, non siamo riusciti a trovare una singola evidenza come quella nel nostro codice sorgente java. Vedi l'esempio @destroys dal codice sorgente.
Suresh Atta

2

Solo per aggiungere un altro esempio in cui il passaggio thisè corretto e segue un buon design: il modello del visitatore . In Visitor design pattern, il metodo accept(Visitor v)viene tipicamente implementato in un modo che chiama semplicemente v.visit(this).


1

Accettabile

Snippet dai documenti Oracle JAVA:

All'interno di un metodo di istanza o di un costruttore, questo è un riferimento all'oggetto corrente, l'oggetto il cui metodo o costruttore viene chiamato. Puoi fare riferimento a qualsiasi membro dell'oggetto corrente dall'interno di un metodo di istanza o di un costruttore usando this.

Usandolo con un campo

Il motivo più comune per utilizzare la parola chiave this è perché un campo è ombreggiato da un metodo o da un parametro del costruttore.


2
"Puoi fare riferimento a qualsiasi membro dell'oggetto corrente " - che non sembra rispondere alla domanda "è accettabile passare thiscome parametro? ".
un CVn

2
Questo sta dicendo come puoi fare this.some_variableper fare riferimento alla variabile di classe invece che a una variabile locale. Non ha nulla a che fare con il passaggio thiscome parametro.
Jose Salvatierra

0

Tutto in java viene passato per valore. Ma gli oggetti non vengono MAI passati al metodo!
Quando java passa un oggetto a un metodo, prima fa una copia di un riferimento all'oggetto, non una copia dell'oggetto stesso. Quindi questo è un metodo perfettamente utilizzato in java. E l'uso più comunemente seguito.


9
Questo sembra argomento.
Denys Séguret

4
"Tutto in java viene passato per valore." - questo è un commento iniziale molto fuorviante. In realtà tutti gli oggetti vengono passati per riferimento e tutti i tipi primitivi vengono passati per valore. Non hai MAI un thisriferimento a un tipo primitivo, e quindi penso che le tue "ulteriori informazioni" stiano introducendo confusione.
Stewart

1
@Stewart: Mette subito in chiaro che non significa che gli oggetti interi vengano copiati.
LarsH

10
@Stewart no, gli oggetti non vengono passati per riferimento, piuttosto i riferimenti agli oggetti vengono passati per valore. È una distinzione importante: passare per riferimento significherebbe che se avessi una variabile locale che si riferisce a un oggetto e passassi quella variabile a un altro metodo, il metodo sarebbe in grado di cambiare l'oggetto a cui si riferisce la mia variabile, e questo sicuramente non è qualcosa puoi farlo in Java. Il metodo può modificare lo stato dell'oggetto tramite la propria copia del riferimento ma non può modificare la mia copia del riferimento in modo che punti a qualcos'altro.
Ian Roberts

2
@Stewart yoda.arachsys.com/csharp/parameters.html è un buon articolo che spiega la differenza tra il passaggio per riferimento e il passaggio di riferimenti per valore, nel contesto di C # che supporta entrambi.
Ian Roberts
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.