Está pasando 'esto' en una llamada a método práctica aceptada en java

93

¿Es una práctica buena / mala / aceptable pasar el objeto actual en una llamada a un método? Como en:

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

Específicamente, ¿es bar.foo(this)aceptable la línea ?

maxf130
fuente
60
¿Por qué eso no sería aceptable? Es común.
Denys Séguret
3
Entonces ... son 8 sí :) (Y sí, lo
mantendré
2
La clase anónima no estática pasa automáticamente esta referencia de la superclase, por lo que será aceptable. la única precaución sería tener cuidado con las referencias circulares.
Mehul Rathod
5
Sin embargo, hay una advertencia: no debe pasar esto en el constructor porque expondría su objeto en un estado inconsistente. La gente suele hacer eso cuando crean devoluciones de llamada (por ejemplo, ActionListener) como clases internas anónimas y luego lo pasan a otro objeto.
Tamas Rev
3
@dystroy aunque estoy de acuerdo en que es aceptable, lo que implica que es aceptable porque es común es realmente una mala lógica. Hacer algo porque es común puede meterte en muchos problemas
Carrie Kendall

Respuestas:

155

No hay razón para no usarlo, thises la instancia actual y su uso es perfectamente legítimo. De hecho, a menudo no existe una forma clara de omitirlo.

Así que úsalo.

Como es difícil convencer de que es aceptable sin un ejemplo (una respuesta negativa a esa pregunta siempre es más fácil de argumentar), acabo de abrir una de las java.langclases más comunes , la Stringúnica, y por supuesto encontré casos de este uso, por ejemplo

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

Busque (thisen grandes proyectos "aceptados", no dejará de encontrarlo.

Denys Séguret
fuente
6
Respuesta perfecta.
maxf130
15
-1 porque no se menciona el hecho de que las relaciones de clase bidireccionales son más complicadas que las relaciones unidireccionales. Es vital asegurarse de que el software sea lo más claro posible. En el ejemplo específico anterior, sería más sensato mover el método foo a la clase Baz para evitar tener una referencia bidireccional entre las dos clases y unir el comportamiento y los datos.
JW.
35
-1 a una respuesta porque encontró en la pregunta un pequeño detalle adicional sobre el que puede comentar? De Verdad ?
Denys Séguret
13
@dystroy Sí, no estoy de acuerdo con tu frase inicial: "No hay razón para no usarla".
JW.
4
En la práctica, en código real (contrariamente al ejemplo simplificado de OP), pasar thisno significa que agregue un enlace bidireccional, debido a la herencia y las interfaces, por ejemplo.
Denys Séguret
165

No hay nada de malo en eso. Lo que NO es una buena práctica es hacer lo mismo dentro de los constructores, porque daría una referencia a un objeto que aún no está completamente inicializado.

Hay una especie de publicación similar aquí: Java filtrando esto en el constructor donde dan una explicación de por qué esta última es una mala práctica.

morgano
fuente
18
+1: es bueno señalar el peligro al referirse a 'esto' en constructores.
Betsabé
No es necesariamente una mala práctica. Por ejemplo, un Carconstructor puede crear Wheelinstancias, un Carsin Wheelse inicializaría de forma incompleta, mientras que un Wheelsin un correspondiente Cartambién se inicializaría de forma incompleta. En este caso, puede ser aceptable en el constructor del automóvil pasar thisal constructor de la rueda. La otra alternativa sería hacer que tanto el automóvil como la rueda tengan un constructor privado y usar una función de fábrica que construya el automóvil, la rueda e instale la rueda en el automóvil; pero ¿debería ser un método estático en el automóvil o un método estático en la rueda?
Lie Ryan
Obviamente, debes crear un CarFactoryWheelInstallerProxyque instale las ruedas por ti.
Kevin
6
@LieRyan Wheelestá completamente subordinado a Car, y la OMI no debería saberlo Caren absoluto.
Izkata
3
Lo ÚNICO malo de usar thisdesde dentro de un constructor es si thisse pasa a un método o contexto desde el cual la referencia del objeto aún no completamente construido se publica para clientes desconocidos o no confiables (o código de cliente que asume que tiene una vista a un objeto completamente construido). Pasar thisde un constructor a un método de paquete privado que realiza una inicialización común es, en mi opinión, no solo aceptable sino deseable.
scottb
42

, pero debes tener cuidado con dos cosas

  1. Pasando esto cuando el objeto aún no se ha construido (es decir, en su constructor)
  2. Pasar esto a un objeto de larga duración, mantendrá viva la referencia y evitará que este objeto sea recolectado como basura.
Stefanos T.
fuente
1
Tenga en cuenta que un constructor en Java no es realmente un constructor, probablemente sea más apropiado llamar al constructor de Java "inicializador". Dentro del constructor de Java, al objeto se le ha asignado memoria, este objeto ya ha existido / construido dentro del constructor.
Lie Ryan
1
Realmente no, el objeto puede tener algunas variables de instancia que aún no se han inicializado, por lo que el objeto aún no está completamente operativo. Entonces, en el constructor, podría pasar esto a un segundo objeto, que podría invocar un método para el objeto que aún no ha inicializado todas sus variables de instancia.
Stefanos T.
sin embargo, siempre que el segundo objeto sea consciente de que el objeto pasado no está inicializado y lo trate como un objeto opaco, o solo llame a métodos que se hayan declarado seguros en tal estado, no causará ningún problema para pasarlo this. Hacer eso hubiera sido imposible si el objeto no hubiera sido asignado.
Lie Ryan
13

Es perfectamente normal y perfectamente aceptable.

Betsabé
fuente
5

esto representa el objeto actual. Lo que está haciendo es sistemáticamente correcto, pero no veo la necesidad de esto si está llamando al método en la misma clase.

Juned Ahsan
fuente
2
En el código de ejemplo, Baz.method () es un método de instancia que llama a Bar.foo () con la instancia de Baz como parámetro. Entonces, el OP no está llamando a un método en la misma clase.
a CVn
@ MichaelKjörling Creo que Juned está diciendo que, al mover el método foo () a la clase Baz, no habría necesidad de pasar thisentre las dos clases. Por lo tanto, no es necesario agregar complejidad adicional.
JW.
4

Es una mala práctica pasar el objeto actual en una llamada a un método si existen alternativas menos complejas para lograr el mismo comportamiento.

Por definición, una asociación bidireccional se crea tan pronto como thisse pasa de un objeto a otro.

Para citar Refactoring, de Martin Fowler:

Cambiar asociación bidireccional a unidireccional (200)

Las asociaciones bidireccionales son útiles, pero tienen un precio. El precio es la complejidad adicional de mantener los enlaces bidireccionales y garantizar que los objetos se creen y eliminen correctamente. Las asociaciones bidireccionales no son naturales para muchos programadores, por lo que a menudo son una fuente de errores.

...

Debe utilizar asociaciones bidireccionales cuando sea necesario, pero no cuando no lo necesite. Tan pronto como vea que una asociación bidireccional ya no está tirando de su peso, deje caer el extremo innecesario.

Entonces, teóricamente, deberíamos escuchar las alarmas cuando nos damos cuenta de que necesitamos aprobar thisy esforzarnos mucho para pensar en otras formas de resolver el problema en cuestión. Por supuesto, hay ocasiones en las que, como último recurso, tiene sentido hacerlo.

Además, a menudo es necesario corromper su diseño temporalmente, haciendo 'cosas de malas prácticas', durante una refactorización a largo plazo de su código para una mejora general. (Un paso atrás, dos pasos adelante).

En la práctica, he descubierto que mi código ha mejorado enormemente al evitar los enlaces bidireccionales como la plaga.

JW.
fuente
Confunde un ejemplo simplificado con la necesidad de establecer un enlace bidireccional. Pasar esto como parámetro, como debería quedar claro con muchos ejemplos del código fuente java.lang (por ejemplo, el que vio en mi respuesta) no significa que agregue una dependencia bidireccional. Esta respuesta debería haber sido un comentario en mi opinión.
Denys Séguret
@dystroy Gracias por agregar un comentario para explicar por qué votó en contra. Siempre es bueno saberlo. Modificaré mi respuesta para aclarar que, por definición, una asociación bidireccional se crea tan pronto como thisse pasa.
JW.
1
"por definición, una asociación bidireccional se crea tan pronto como se pasa" . Esto deja en claro dónde no logra comprender. Mira el ejemplo que doy. No hay un enlace bidireccional porque el tipo de argumento en equalses Object. Esto es muy común: el método de recepción define su argumento como una clase más general o como una interfaz. Una de las razones por las que se utiliza este patrón en Java es para evitar dependencias no deseadas. Antes de continuar, le sugiero que eche un vistazo a las muchas ocurrencias de pasar thiscomo argumento en bibliotecas Java respetables.
Denys Séguret
Aceptemos estar en desacuerdo. Mi vida se ha vuelto mucho más fácil desde que he evitado pasar thismi código, siempre que sea posible. Recomendaría a otros que lo hicieran.
JW.
2
@JW: el razonamiento dado en esta respuesta es irrelevante. Siempre es una mala idea hacer X si hay algo más simple, por cualquier valor de X.
Lie Ryan
4

Si. puede usarlo. Es común en la programación pasar. thisPero hay pros y contras acerca de usarlo. Aún así, no es peligroso hacerlo.

Suresh Atta
fuente
Hay muchos efectos secundarios. Agrega complejidad.
JW.
Si esos muchos efectos secundarios están ahí, no podríamos encontrar una sola evitación como esa en nuestro código fuente de Java. Vea el ejemplo de @destroys del código fuente.
Suresh Atta
2

Solo para agregar un ejemplo más en el que pasar thises correcto y sigue un buen diseño: Patrón de visitante . En el patrón de diseño de visitantes, el método accept(Visitor v)generalmente se implementa de una manera que simplemente llama v.visit(this).

Petr Zelenka
fuente
1

Aceptable

Fragmento de documentos de Oracle JAVA:

Dentro de un método de instancia o un constructor, esta es una referencia al objeto actual, el objeto cuyo método o constructor se está llamando. Puede hacer referencia a cualquier miembro del objeto actual desde dentro de un método de instancia o un constructor usando this.

Usando esto con un campo

La razón más común para usar la palabra clave this es porque un campo está sombreado por un método o parámetro de constructor.

Nargis
fuente
2
"Puede hacer referencia a cualquier miembro del objeto actual "; eso no parece responder a la pregunta "¿es aceptable pasar thiscomo parámetro? ".
a CVn
2
Esto dice cómo puede hacer this.some_variablepara referirse a la variable de clase en lugar de a una variable local. No tiene nada que ver con pasar thiscomo parámetro.
Jose Salvatierra
0

Todo en Java se pasa por valor. ¡Pero los objetos NUNCA se pasan al método!
Cuando Java pasa un objeto a un método, primero hace una copia de una referencia al objeto, no una copia del objeto en sí. Por lo tanto, este es un método perfectamente utilizado en Java. Y el uso más seguido.

blganesh101
fuente
9
Esto parece de tema.
Denys Séguret
4
"Todo en Java se pasa por valor". - ese es un comentario inicial muy engañoso. En realidad, todos los objetos se pasan por referencia y todos los tipos primitivos se pasan por valor. NUNCA ha hecho thisreferencia a un tipo primitivo y, por lo tanto, creo que su "más información" está introduciendo confusión.
Stewart
1
@ Stewart: Inmediatamente deja en claro que no quiere decir que se copien objetos completos.
LarsH
10
@Stewart no, los objetos no se pasan por referencia, sino que las referencias a objetos se pasan por valor. Es una distinción importante: pasar por referencia significaría que si tengo una variable local que se refiere a un objeto y paso esa variable a otro método, el método podría cambiar a qué objeto se refiere mi variable, y esto definitivamente no es algo que puede hacer en Java. El método puede mutar el estado del objeto a través de su propia copia de la referencia, pero no puede cambiar mi copia de la referencia para que apunte a otra cosa.
Ian Roberts
2
@Stewart yoda.arachsys.com/csharp/parameters.html es un buen artículo que explica la diferencia entre pasar por referencia y pasar referencias por valor, en el contexto de C # que admite ambos.
Ian Roberts