Referência circular

Pessoal

Tenho um objeto A, que acessa métodos de outro objeto B. Porém, este objeto B também precisa da referência de A.

Sempre li que deve-se evitar isto:

class A {

   private B b;

   public A(){
      this.b = new B(this);
   }
}

class B {

   private A a;

   public B(A a){
      this.a = a;
   }
}

Como posso fazer neste caso?
Em alguns lugares, li que é melhor passar a referência do this em um método set e não no construtor, mas isto não dá na mesma?

Não vejo problema algum no seu código, inclusive isso aí é feito para navegação em persistências de tabela, tipo:

[code]class TabelaPai{
private TabelaFilha tf;
public getTF(){return tf;}
}

class TabelaFilha{
private TabelaPai tp;
public TabelaFilha(TabelaPai tp){this.tp = tp}
}[/code]

Além disso ser utilizado tb em listas duplamente encadeadas…

o q deve ser evitado é chamadas que possam resultar em deadlock, tipo: TP chama TF que dentro do próprio método chama TP d novo q chama TF…

Isso deve ser evitado pq B receberá uma instância de um A que não foi completamente construido. Se seu construtor se resumir a fazer essa atribuição, então, ok, passa.

Se não, você teria que ter um método fábrica:

public class A {
   private A() {
   }

   private void setB(B b) {
      this.b = b;
   }

   public static A create() {
      A a = new A();
      B b = new B(a);
      a.setB(b);
      return a;
   }
}

Obrigado pelas respostas, pessoal.

Só fiquei com uma dúvida:

[quote=ViniGodoy]Isso deve ser evitado pq B receberá uma instância de um A que não foi completamente construido. Se seu construtor se resumir a fazer essa atribuição, então, ok, passa.

Se não, você teria que ter um método fábrica:

[code]
public class A {
private A() {
}

private void setB(B b) {
this.b = b;
}

public static A create() {
A a = new A();
B b = new B(a);
a.setB(b);
return a;
}
}
[/code][/quote]

Mesmo que a instância de A não esteja completamente construída quando eu inicializar B, eu vou inicializar B passando uma referência para esta instância de A. Neste caso, mesmo que eu continua construindo A depois de inicializar B, a referência continuará sendo a mesma, o que não geraria problemas, não?

Por exemplo:

class A {

   private B b;
   private String str1;

   public String getStr1(){
      return str1;
   }

   public A(){
      this.b = new B(this); 
      // neste momento, estou inicializando B, e a variável str1 de A está nula

      str1 = "123";
      // neste momento, atualizei o valor de str1 para a String "123". 
      // levando em conta que a referência de b para este A ainda é a mesma, se eu chamar getStr1() de b, o retorno vai ser "123", não?
   }
}

class B {

   private A a;

   public B(A a){
      this.a = a;
   }
}

Ou existe algum outro problema que pode ser gerado?

[quote=diegogmarques]Pessoal

Tenho um objeto A, que acessa métodos de outro objeto B. Porém, este objeto B também precisa da referência de A.

Sempre li que deve-se evitar isto:

class A {

   private B b;

   public A(){
      this.b = new B(this);
   }
}

class B {

   private A a;

   public B(A a){
      this.a = a;
   }
}

Como posso fazer neste caso?
Em alguns lugares, li que é melhor passar a referência do this em um método set e não no construtor, mas isto não dá na mesma?[/quote]

Se x= 2, e y =3 como trocar os valores ?

A resposta é : vc precisa de um terceiro cara.
Em objetos isso é um proxy.

Vc cria uma implementação de A, ProxyA que contém A, mas começa vazio

ai vc faz

ProxyA pa = new ProxyA()

como ProxyA herda A, vc faz

B b = new B(pa);

B está agora complementa construido e inicializado.
Agora vc faz

A a = new A(b);

A está agora completamente construido.

finalmente vc amarra as pontas

pa.setA(a);

Agora vc pode chamar métodos de B porque eles irão para em A.
Versões mais sofisticadas podem ser feitas, em particular fazer ou utilziar um injeto de dependencia que consiga lidar com circularidade.

Sabe, eu gosto d desafios e não pude deixar de tentar…

A resposta é: XOR 3x

public static void permuta() { int x = 2; int y = 3; System.out.format("x= %s: y= %s\n", x, y); x = x ^ y; y = x ^ y; x = x ^ y; System.out.format("x= %s: y= %s\n", x, y); }

[quote=diegogmarques]Mesmo que a instância de A não esteja completamente construída quando eu inicializar B, eu vou inicializar B passando uma referência para esta instância de A. Neste caso, mesmo que eu continua construindo A depois de inicializar B, a referência continuará sendo a mesma, o que não geraria problemas, não?
[/quote]

E porque teria? O problema não está necessariamente em cruzar referências, mas na forma como isso é feito, ou seja, em passar um objeto A parcialmente construído para B, na hora de fazer isso.

Não entendi o seu exemplo. O getStr() refere-se a uma variável local, e não faz qualquer menção com a referência. O que ele tem a ver com A?

No caso, você não deve passar o this como parâmetro numa construção por causa disso aqui:

[code]
public class A {
private int a;
private String x;
private B b;

public A() {
b = new B(this);
a = 10;
x = “Vinicius”;
}

//gets e sets

}

public class B {
private int c;

public void B(A a) {
    c = a.getA() * 2;
}

//gets e sets

} [/code]

Pergunta-se. Qual é o valor que método getA() retornará, quando B fizer a chamada?

A coisa pode ficar ainda pior se A tiver subclasses e B chamar um método sobrescrito de A. Por exemplo, suponha que você tenha “corrigido” a classe A, e colocado a inicialização de B na última linha. Aí fica correto, certo?

[code]
public class A {
private int a;
private String x;
private B b;

public A() {
a = 10;
x = “Vinicius”;
b = new B(this);
}

//gets e sets

}[/code]

Agora, imagine que posteriormente alguém cria a classe C, dessa forma:

[code]public class C extends A {
private int x;
public C() {
this.x = 20;
}

public void getA() {
return super.getA() * x;
}
}[/code]

O que acontece quando criamos os objetos assim?

A a = new C();

Note que agora o construtor de B irá novamente tentar chamar o método getA(). Aparentemente está tudo certo, mas o método sobrescrito precisará do valor de x, que não foi inicializado ainda, já que a construção ocorre de cima para baixo e o construtor da classe C, portanto, não foi chamado.

É uma má prática passar objeto incompleto para B, porque quem quer que programe B deve ter como pressuposto que A é um objeto válido, e completamente construído. Quando você troca referências da forma que você mostrou, esse pressuposto é inválido, o que pode gerar erros de programação difíceis de corrigir. O comportamento do Java nessas situações é indefinido.

[quote=barenko][quote=sergiotaborda]
Se x= 2, e y =3 como trocar os valores ?

A resposta é : vc precisa de um terceiro cara.
[/quote]

Sabe, eu gosto d desafios e não pude deixar de tentar…

A resposta é: XOR 3x

public static void permuta() { int x = 2; int y = 3; System.out.format("x= %s: y= %s\n", x, y); x = x ^ y; y = x ^ y; x = x ^ y; System.out.format("x= %s: y= %s\n", x, y); }[/quote]

Leia o Efective Java e Java Puzzles para ver pq isso é uma horrivel ideia.