Passe 'this' dans un appel de méthode pratique acceptée en Java


93

Est-ce une pratique bonne / mauvaise / acceptable de passer l'objet courant dans un appel de méthode. Un péché:

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);
    }
}

Plus précisément, la ligne est-elle bar.foo(this)acceptable?


60
Pourquoi cela ne serait-il pas acceptable? C'est courant.
Denys Séguret

3
Donc ... c'est 8 oui :) (Et oui, je le tiens à jour.)
Alex Gittemeier

2
la classe anonyme non statique passe automatiquement cette référence de la super classe, donc cela sera acceptable. la seule mise en garde serait de faire attention aux références circulaires.
Mehul Rathod

5
Il y a cependant une mise en garde: vous ne devriez pas passer ceci dans le constructeur car cela exposerait votre objet dans un état incohérent. Les gens font généralement cela lorsqu'ils créent des rappels (par exemple ActionListener) en tant que classes internes anonymes, puis les transmettent à un autre objet.
Tamas Rev

3
@dystroy même si je suis d'accord que c'est acceptable, ce qui implique que c'est acceptable parce que c'est courant est vraiment une mauvaise logique. Faire quelque chose parce que c'est commun peut vous causer beaucoup d'ennuis
Carrie Kendall

Réponses:


155

Il n'y a aucune raison de ne pas l'utiliser, thisc'est l'instance actuelle et son utilisation est parfaitement légitime. En fait, il n'y a souvent aucun moyen propre de l'omettre.

Alors utilisez-le.

Comme il est difficile de convaincre que c'est acceptable sans exemple (une réponse négative à une telle question est toujours plus facile à argumenter), je viens d'ouvrir l'une des java.langclasses les plus courantes , Stringcelle, et bien sûr j'ai trouvé des instances de cette utilisation, par exemple

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

Cherchez (thisdans les grands projets «acceptés», vous ne manquerez pas de le trouver.


6
Réponse parfaite.
maxf130

15
-1 parce qu'il n'est pas fait mention du fait que les relations de classe bidirectionnelles sont plus compliquées que les relations unidirectionnelles. Il est essentiel de s'assurer que le logiciel est aussi clair que possible. Dans l'exemple spécifique ci-dessus, il serait plus judicieux de déplacer la méthode foo vers la classe Baz pour éviter d'avoir une référence bidirectionnelle entre les deux classes et pour rassembler le comportement et les données.
JW.

35
-1 à une réponse parce que vous avez trouvé dans la question un détail mineur supplémentaire sur lequel vous pouvez commenter? Vraiment ?
Denys Séguret

13
@dystroy Oui, je ne suis pas d'accord avec votre phrase d'ouverture: "Il n'y a aucune raison de ne pas l'utiliser".
JW.

4
En pratique, dans le code réel (contrairement à l'exemple simplifié d'OP), passer thisne signifie pas ajouter un lien bidirectionnel, à cause de l'héritage et des interfaces par exemple.
Denys Séguret

165

Il n'y a rien de mal à cela. Ce qui n'est PAS une bonne pratique, c'est de faire la même chose à l'intérieur des constructeurs, car vous donneriez une référence à un objet pas encore complètement initialisé.

Il y a une sorte de post similaire ici: Java divulgue ceci dans le constructeur où ils expliquent pourquoi ce dernier est une mauvaise pratique.


18
+1: il est bon de souligner le danger en se référant à «ceci» chez les constructeurs.
Bathsheba

Ce n'est pas nécessairement une mauvaise pratique. Par exemple, un Carconstructeur peut créer des Wheelinstances, un Carwithout Wheelserait incomplètement initialisé, tandis qu'un Wheelsans un correspondant Carserait également incomplètement initialisé. Dans ce cas, il peut être acceptable dans le constructeur de la voiture de passer thisau constructeur de la roue. L'autre alternative serait de faire en sorte que la voiture et la roue aient un constructeur privé et utilisent une fonction d'usine qui construit la voiture, la roue et installe la roue sur la voiture; mais cela devrait-il être une méthode statique sur Car ou une méthode statique sur Wheel?
Lie Ryan

De toute évidence, vous devez créer un système CarFactoryWheelInstallerProxyqui installe les roues pour vous.
Kevin

6
@LieRyan Wheelest complètement subordonné à Car, et l'OMI ne devrait pas du tout savoir Car.
Izkata le

3
La SEULE mauvaise chose à propos de l'utilisation à thispartir d'un constructeur est si thisest passé dans une méthode ou un contexte à partir duquel la référence d'objet pas encore entièrement construite est publiée sur des clients non approuvés ou inconnus (ou un code client qui suppose qu'il a une vue sur un objet entièrement construit). Passer thisd'un constructeur à une méthode privée de package qui effectue une initialisation commune est, à mon avis, non seulement acceptable mais souhaitable.
scottb

42

Oui , mais vous devez faire attention à deux choses

  1. Passer ceci lorsque l'objet n'a pas encore été construit (c'est-à-dire dans son constructeur)
  2. Passer ceci à un objet de longue durée, qui maintiendra la référence en vie et empêchera cet objet d'être ramassé.

1
Notez qu'un constructeur en Java n'est pas vraiment un constructeur, il est probablement plus approprié d'appeler le constructeur de Java "initializer". Dans le constructeur Java, l'objet a en fait été alloué de la mémoire, cet objet a en fait déjà existé / construit dans le constructeur.
Lie Ryan

1
Pas vraiment, l'objet peut avoir des variables d'instance qui n'ont pas encore été initialisées, de sorte que l'objet n'est pas encore pleinement opérationnel. Ainsi, dans le constructeur, vous pouvez passer ceci à un deuxième objet, qui pourrait appeler une méthode à l'objet qui n'a pas encore initialisé toutes ses variables d'instance.
Stefanos T.

cependant, tant que le deuxième objet est conscient que l'objet passé n'est pas initialisé, et le traite comme un objet opaque, ou n'appelle que des méthodes qui ont été déclarées sûres dans un tel état, il ne posera aucun problème à lui passer this. Faire cela aurait été impossible si l'objet n'avait pas été alloué.
Lie Ryan


5

cela représente l'objet actuel. Ce que vous faites est statiquement correct, mais je ne vois pas la nécessité de cela si vous appelez la méthode dans la même classe.


2
Dans l'exemple de code, Baz.method () est une méthode d'instance qui appelle Bar.foo () avec l'instance de Baz comme paramètre. Ainsi, l'OP n'appelle pas une méthode de la même classe.
un CVn le

@ MichaelKjörling Je pense que Juned dit qu'en déplaçant la méthode foo () dans la classe Baz, il n'y aurait pas besoin de passer thisentre les deux classes. Il n'est donc pas nécessaire d'ajouter la complexité supplémentaire.
JW.

4

C'est une mauvaise pratique de passer l'objet courant dans un appel de méthode s'il existe des alternatives moins complexes pour obtenir le même comportement.

Par définition, une association bidirectionnelle est créée dès qu'elle thisest passée d'un objet à un autre.

Pour citer Refactoring, par Martin Fowler:

Changer l'association bidirectionnelle en unidirectionnel (200)

Les associations bidirectionnelles sont utiles, mais elles ont un prix. Le prix est la complexité supplémentaire de maintenir les liens bidirectionnels et de s'assurer que les objets sont correctement créés et supprimés. Les associations bidirectionnelles ne sont pas naturelles pour de nombreux programmeurs, elles sont donc souvent une source d'erreurs

...

Vous devez utiliser des associations bidirectionnelles lorsque vous en avez besoin, mais pas lorsque vous ne le faites pas. Dès que vous voyez qu'une association bidirectionnelle ne tire plus son poids, laissez tomber l'extrémité inutile.

Donc, théoriquement, nous devrions entendre la sonnette d'alarme lorsque nous constatons que nous devons passer thiset essayer vraiment de penser à d'autres moyens de résoudre le problème en cours. Il y a, bien sûr, des moments où, en dernier ressort, il est logique de le faire.

De plus, il est souvent nécessaire de corrompre temporairement votre conception, en faisant des «mauvaises pratiques», lors d'une refactorisation à plus long terme de votre code pour une amélioration globale. (Un pas en arrière, deux pas en avant).

Dans la pratique , j'ai trouvé mon code a amélioré massivement en évitant des liens bidirectionnels comme la peste.


Vous confondez un exemple simplifié avec la nécessité d'établir un lien bidirectionnel. Passer ceci comme paramètre, comme cela devrait être clair avec de nombreux exemples du code source java.lang (par exemple celui que vous avez vu dans ma réponse) ne signifie pas que vous ajoutez une dépendance bidirectionnelle. Cette réponse aurait dû être un commentaire à mon avis.
Denys Séguret

@dystroy Merci d'avoir ajouté un commentaire pour expliquer pourquoi vous avez voté contre. C'est toujours bon à savoir. Je modifierai ma réponse pour préciser que, par définition, une association bidirectionnelle est créée dès qu'elle thisest passée.
JW.

1
"par définition, une association bidirectionnelle est créée dès qu'elle est passée" . Cela montre clairement où vous ne comprenez pas. Regardez l'exemple que je donne. Il n'y a pas de lien bidirectionnel car le type de l'argument dans equalsest Object. C'est très courant: la méthode réceptrice définit son argument comme une classe plus générale ou comme une interface. L'une des raisons pour lesquelles un tel modèle est utilisé en java est d'éviter les dépendances indésirables. Avant d'aller plus longtemps, je vous suggère de jeter un coup d'œil aux nombreuses occurrences de passage thisen argument dans des bibliothèques Java respectables.
Denys Séguret

Acceptons simplement de ne pas être d'accord. Ma vie est devenue beaucoup plus facile depuis que j'ai évité de passer thismon code, lorsque c'était possible. Je recommanderais à d'autres de le faire.
JW.

2
@JW: le raisonnement donné dans cette réponse n'est pas pertinent. C'est toujours une mauvaise idée de faire X s'il y a autre chose de plus simple, quelle que soit la valeur de X.
Lie Ryan

4

Oui. vous pouvez l'utiliser.Il est juste courant dans la programmation de passer this.Mais il y a des avantages et des inconvénients à l'utiliser.Mais ce n'est pas dangereux de le faire.


Il existe de nombreux effets secondaires. Cela ajoute de la complexité.
JW.

Si ces nombreux effets secondaires sont là, nous n'avons pas pu trouver une seule preuve comme celle-ci dans notre code source java .Voir l'exemple @destroys du code source.
Suresh Atta

2

Juste pour ajouter un autre exemple où le passage thisest correct et suit une bonne conception: modèle de visiteur . Dans le modèle de conception de visiteur, la méthode accept(Visitor v)est généralement implémentée d'une manière qu'elle appelle simplement v.visit(this).


1

Acceptable

Extrait de la documentation Oracle JAVA:

Dans une méthode d'instance ou un constructeur, il s'agit d'une référence à l'objet actuel - l'objet dont la méthode ou le constructeur est appelé. Vous pouvez faire référence à n'importe quel membre de l'objet actuel à partir d'une méthode d'instance ou d'un constructeur en utilisant this.

Utiliser ceci avec un champ

La raison la plus courante d'utilisation du mot-clé this est qu'un champ est masqué par une méthode ou un paramètre de constructeur.


2
"Vous pouvez faire référence à n'importe quel membre de l'objet courant " - cela ne semble pas répondre à la question "est-il acceptable de passer thisen paramètre? ".
un CVn le

2
Cela indique comment vous pouvez faire this.some_variablepour faire référence à la variable de classe plutôt qu'à une variable locale. Cela n'a rien à voir avec le passage thisen paramètre.
Jose Salvatierra

0

Tout en java est passé par valeur. Mais les objets ne sont JAMAIS passés à la méthode!
Lorsque java passe un objet à une méthode, il effectue d'abord une copie d'une référence à l'objet, pas une copie de l'objet lui-même. C'est pourquoi cette méthode est parfaitement utilisée en java. Et l'utilisation la plus couramment suivie.


9
Cela ressemble à un sujet.
Denys Séguret

4
"Tout en java est passé par valeur." - c'est un commentaire initial très trompeur. En fait, tous les objets sont passés par référence et tous les types primitifs sont passés par valeur. Vous n'avez JAMAIS de thisréférence à un type primitif, et donc je pense que votre "plus d'infos" introduit de la confusion.
Stewart le

1
@Stewart: Il précise immédiatement qu'il ne veut pas dire que des objets entiers sont copiés.
LarsH

10
@Stewart non, les objets ne sont pas passés par référence, mais les références d'objet sont passées par valeur. C'est une distinction importante - passer par référence signifierait que si j'ai une variable locale qui fait référence à un objet et que je passe cette variable à une autre méthode, la méthode pourrait changer à quel objet ma variable se réfère, et ce n'est certainement pas quelque chose vous pouvez le faire en Java. La méthode peut muter l'état de l'objet via sa propre copie de la référence, mais elle ne peut pas changer ma copie de la référence pour qu'elle pointe vers autre chose.
Ian Roberts

2
@Stewart yoda.arachsys.com/csharp/parameters.html est un bon article qui explique la différence entre passer par référence et passer des références par valeur, dans le contexte de C # qui prend en charge les deux.
Ian Roberts
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.