Está passando 'isto' em uma prática aceita de chamada de método em java

93

É uma prática boa / ruim / aceitável passar o objeto atual em uma chamada de método. Como em:

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

Especificamente, a linha é bar.foo(this)aceitável?

maxf130
fonte
60
Por que isso não seria aceitável? É comum.
Denys Séguret
3
Então ... são 8 sim :) (E sim, estou mantendo-o atualizado.)
Alex Gittemeier
2
classe anônima não estática passa automaticamente esta referência da superclasse, então isso será aceitável. o único cuidado seria tomar cuidado com as referências circulares.
Mehul Rathod
5
Porém, há uma advertência: você não deve passar isso no construtor porque isso exporia seu objeto em um estado inconsistente. As pessoas normalmente fazem isso quando criam retornos de chamada (por exemplo, ActionListener) como classes internas anônimas e, em seguida, passam isso para outro objeto.
Rev Tamas
3
@dystroy embora eu concorde que é aceitável, o que implica que é aceitável porque é comum é uma lógica muito ruim. Fazer algo porque é comum pode causar muitos problemas
Carrie Kendall

Respostas:

155

Não há razão para não usá-lo, thisé a instância atual e é perfeitamente legítimo para usar. Na verdade, geralmente não há uma maneira limpa de omiti-lo.

Então use.

Como é difícil convencer que é aceitável sem exemplo (uma resposta negativa a essa pergunta é sempre mais fácil de argumentar), acabei de abrir uma das java.langclasses mais comuns , a String, e é claro que encontrei exemplos desse uso, por exemplo

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

Procure (thisem grandes projetos "aceitos", você não deixará de encontrá-lo.

Denys Séguret
fonte
6
Resposta perfeita.
maxf130
15
-1 porque não há menção do fato de que os relacionamentos de classe bidirecionais são mais complicados do que os relacionamentos unidirecionais. É vital garantir que o software seja o mais claro possível. No exemplo específico acima, seria mais sensato mover o método foo para a classe Baz para evitar ter uma referência bidirecional entre as duas classes e para trazer o comportamento e os dados juntos.
JW.
35
-1 para uma resposta porque você encontrou na pergunta um pequeno detalhe adicional sobre o qual você pode comentar? Sério ?
Denys Séguret
13
@dystroy Sim, discordo da sua frase inicial: "Não há razão para não usar".
JW.
4
Na prática, no código real (ao contrário do exemplo simplificado do OP), passar thisnão significa adicionar um link bidirecional, por causa da herança e das interfaces, por exemplo.
Denys Séguret
165

Não há nada de errado nisso. O que NÃO é uma boa prática é fazer o mesmo dentro dos construtores, porque você daria uma referência a um objeto ainda não completamente inicializado.

Há um tipo de postagem semelhante aqui: Java vazando isso no construtor, onde eles dão uma explicação de por que o último é uma prática ruim.

Morgano
fonte
18
+1: bom apontar o perigo em se referir a 'isto' em construtores.
Bathsheba
Não é necessariamente uma prática ruim. Por exemplo, um Carconstrutor pode criar Wheelinstâncias, um Carsem Wheelseria inicializado de forma incompleta, enquanto um Wheelsem um correspondente Cartambém seria inicializado de forma incompleta. Nesse caso, pode ser aceitável no construtor do carro passar thispara o construtor da roda. A outra alternativa seria fazer com que o carro e a roda tivessem um construtor privado e usar uma função de fábrica que constrói o carro, a roda e instala a roda no carro; mas deveria ser um método estático em Car ou um método estático em Wheel?
Lie Ryan
Obviamente, você deve criar um CarFactoryWheelInstallerProxyque instale as rodas para você.
Kevin
6
@LieRyan Wheelé totalmente subordinado a Car, e a IMO não deveria saber disso Car.
Izkata
3
A ÚNICA coisa ruim de usar thisde dentro de um construtor é se thisfor passado para um método ou contexto a partir do qual a referência de objeto ainda não totalmente construída é publicada para clientes não confiáveis ​​ou desconhecidos (ou código de cliente que assume que tem uma visão para um objeto totalmente construído). Passar thisde um construtor para um método privado de pacote que executa uma inicialização comum é, na minha opinião, não apenas aceitável, mas desejável.
scottb
42

Sim , mas você deve ter cuidado com duas coisas

  1. Passando este quando o objecto não tiver sido ainda construído (ou seja, no seu construtor)
  2. Passar isso para um objeto de longa vida, que manterá a referência viva e impedirá que este objeto seja coletado como lixo.
Stefanos T.
fonte
1
Observe que um construtor em Java não é realmente um construtor, provavelmente é mais apropriado chamar o construtor Java de "inicializador". Dentro do construtor Java, o objeto realmente tem memória alocada, este o objeto realmente já existiu / construído dentro do construtor.
Lie Ryan
1
Na verdade não, o objeto pode ter algumas variáveis ​​de instância que ainda não foram inicializadas, de modo que o objeto ainda não está totalmente operacional. Portanto, no construtor, você poderia passar isso para um segundo objeto, que poderia invocar um método para o objeto que ainda não inicializou todas as suas variáveis ​​de instância.
Stefanos T.
ainda assim, contanto que o segundo objeto esteja ciente de que o objeto passado não foi inicializado e o trate como um objeto opaco, ou apenas chame métodos que foram declarados seguros em tal estado, não causará nenhum problema para passar thispara ele. Isso teria sido impossível se o objeto não tivesse sido alocado.
Lie Ryan
13

É perfeitamente normal e perfeitamente aceitável.

Bate-Seba
fonte
5

isso representa o objeto atual. O que você está fazendo é sistematicamente correto, mas não vejo necessidade disso se você estiver chamando o método na mesma classe.

Juned Ahsan
fonte
2
No código de exemplo, Baz.method () é um método de instância que chama Bar.foo () com a instância de Baz como parâmetro. Portanto, o OP não está chamando um método da mesma classe.
um CVn de
@ MichaelKjörling Acho que Juned está dizendo que, movendo o método foo () para a classe Baz, não haveria necessidade de passar thisentre as duas classes. Portanto, não há necessidade de adicionar complexidade extra.
JW.
4

É uma má prática passar o objeto atual em uma chamada de método se houver alternativas menos complexas para obter o mesmo comportamento.

Por definição, uma associação bidirecional é criada assim que thisé passada de um objeto para outro.

Para citar Refatoração, por Martin Fowler:

Alterar associação bidirecional para unidirecional (200)

As associações bidirecionais são úteis, mas têm um preço. O preço é a complexidade adicional de manter os links bidirecionais e garantir que os objetos sejam criados e removidos corretamente. As associações bidirecionais não são naturais para muitos programadores, então muitas vezes são uma fonte de erros

...

Você deve usar associações bidirecionais quando for necessário, mas não quando não for necessário. Assim que você perceber que uma associação bidirecional não está mais exercendo sua influência, descarte a extremidade desnecessária.

Então, teoricamente, deveríamos estar ouvindo alarmes quando acharmos que precisamos passar thise tentar realmente pensar em outras maneiras de resolver o problema em questão. É claro que há momentos em que, em último recurso, faz sentido fazer isso.

Também é frequentemente necessário corromper seu design temporariamente, fazendo 'coisas de prática ruim', durante uma refatoração de longo prazo de seu código para uma melhoria geral. (Um passo para trás, dois passos para a frente).

Na prática, descobri que meu código melhorou muito , evitando links bidirecionais como a praga.

JW.
fonte
Você confunde um exemplo simplificado com a necessidade de estabelecer um link bidirecional. Passar isso como parâmetro, como deve ficar claro com muitos exemplos do código-fonte java.lang (por exemplo, o que você viu em minha resposta), não significa que você adiciona uma dependência bidirecional. Esta resposta deveria ter sido um comentário em minha opinião.
Denys Séguret
@dystroy Obrigado por adicionar um comentário para explicar porque você votou contra. É sempre bom saber. Vou modificar minha resposta para esclarecer que, por definição, uma associação bidirecional é criada assim que thisé aprovada.
JW.
1
"por definição, uma associação bidirecional é criada assim que isso é passado" . Isso deixa claro onde você não consegue entender. Veja o exemplo que dou. Não há link bidirecional porque o tipo do argumento em equalsé Objeto. Isso é muito comum: o método receptor define seu argumento como uma classe mais geral ou como uma interface. Uma das razões pelas quais esse padrão é usado em java é para evitar dependências indesejadas. Antes de prosseguir, sugiro que você dê uma olhada nas muitas ocorrências de passagem thiscomo argumento em bibliotecas Java respeitáveis.
Denys Séguret
Vamos apenas concordar em discordar. Minha vida se tornou muito mais fácil desde que evitei passar thismeu código, sempre que possível. Eu recomendaria que outros o fizessem.
JW.
2
@JW: o raciocínio dado nesta resposta é irrelevante. É sempre uma má ideia fazer X se houver algo mais simples, para qualquer valor de X.
Lie Ryan
4

Sim. você pode usá-lo. É comum na programação passar this. Mas há prós e contras em usar isso. Ainda assim, não é perigoso fazer isso.

Suresh Atta
fonte
Existem muitos efeitos colaterais. Isso adiciona complexidade.
JW.
Se esses muitos efeitos colaterais estiverem presentes, não poderíamos encontrar uma única evidência como essa em nosso código-fonte Java. Veja o exemplo @destroys no código-fonte.
Suresh Atta
2

Apenas para adicionar mais um exemplo em que a passagem thisé correta e segue um bom design: Padrão de visitante . No padrão de design do Visitante, o método accept(Visitor v)é geralmente implementado de uma maneira que ele apenas chama v.visit(this).

Petr Zelenka
fonte
1

Aceitável

Snippet de documentos Oracle JAVA:

Dentro de um método de instância ou construtor, esta é uma referência ao objeto atual - o objeto cujo método ou construtor está sendo chamado. Você pode se referir a qualquer membro do objeto atual de dentro de um método de instância ou um construtor usando this.

Usando isso com um campo

O motivo mais comum para usar a palavra-chave this é porque um campo é sombreado por um método ou parâmetro do construtor.

Nargis
fonte
2
“Você pode se referir a qualquer membro do objeto atual ” - isso não parece responder à pergunta “é aceitável passar thiscomo parâmetro? ”.
um CVn de
2
Isso está dizendo como você pode fazer this.some_variablepara se referir à variável de classe em vez de uma variável local. Não tem nada a ver com passar thiscomo parâmetro.
José Salvatierra
0

Tudo em java é passado por valor. Mas os objetos NUNCA são passados ​​para o método!
Quando java passa um objeto para um método, ele primeiro faz uma cópia de uma referência ao objeto, não uma cópia do próprio objeto. Portanto, este é um método perfeitamente usado em java. E o uso mais comumente seguido.

blganesh101
fonte
9
Isso parece assunto.
Denys Séguret
4
"Tudo em java é passado por valor." - esse é um comentário inicial muito enganador. Na verdade, todos os objetos são passados ​​por referência e todos os tipos primitivos são passados ​​por valor. Você NUNCA fez thisreferência a um tipo primitivo e, portanto, acho que suas "mais informações" estão causando confusão.
Stewart
1
@Stewart: Ele imediatamente deixa claro que não quer dizer que objetos inteiros sejam copiados.
LarsH
10
@Stewart não, os objetos não são passados ​​por referência, em vez disso, as referências a objetos são passadas por valor. É uma distinção importante - passar por referência significaria que se eu tivesse uma variável local que se refere a um objeto e passasse essa variável para outro método, o método seria capaz de alterar a qual objeto minha variável se refere, e isso definitivamente não é algo você pode fazer em Java. O método pode alterar o estado do objeto por meio de sua própria cópia da referência, mas não pode alterar minha cópia da referência para apontar para outra coisa.
Ian Roberts
2
@Stewart yoda.arachsys.com/csharp/parameters.html é um bom artigo que explica a diferença entre passar por referência e passar referências por valor, no contexto de C # que oferece suporte a ambos.
Ian Roberts