unir - Dependencia circular en clases de java.
llamar funcion desde otra clase java (6)
El constructor de su clase A llama al constructor de la clase B. El constructor de la clase B llama al constructor de la clase A. Usted tiene una llamada de recursión infinita, por eso termina teniendo un StackOverflowError
.
Java admite tener dependencias circulares entre clases, el problema aquí solo está relacionado con los constructores que se llaman entre sí.
Puedes probar con algo como:
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
Tengo las siguientes clases.
public class B
{
public A a;
public B()
{
a= new A();
System.out.println("Creating B");
}
}
y
public class A
{
public B b;
public A()
{
b = new B();
System.out.println("Creating A");
}
public static void main(String[] args)
{
A a = new A();
}
}
Como puede verse claramente, existe una dependencia circular entre las clases. si trato de ejecutar la clase A, finalmente obtendré un StackOverflowError
.
Si se crea un gráfico de dependencia, donde los nodos son clases, entonces esta dependencia se puede identificar fácilmente (al menos para los gráficos con pocos nodos). Entonces, ¿por qué la JVM no identifica esto, al menos en tiempo de ejecución? En lugar de lanzar un StackOverflowError
, JVM puede al menos dar una advertencia antes de iniciar la ejecución.
[Actualización] Algunos idiomas no pueden tener dependencias circulares, porque entonces el código fuente no se compilará. Por ejemplo, vea esta pregunta y la respuesta aceptada. Si la dependencia circular es un olor de diseño para C #, ¿por qué no es para Java? ¿Solo porque Java puede (compilar código con dependencias circulares)?
[update2] jCarder encontrado jCarder . Según el sitio web, encuentra puntos muertos potenciales al instrumentar dinámicamente los códigos de bytes de Java y buscar ciclos en el gráfico de objetos. ¿Alguien puede explicar cómo la herramienta encuentra los ciclos?
Es perfectamente válido en Java tener una relación circular entre 2 clases (aunque se pueden hacer preguntas sobre el diseño), sin embargo, en su caso, tiene la acción inusual de cada instancia, creando una instancia de la otra en su constructor (este es el verdadero causa del Error).
Este patrón particular se conoce como una recursión mutua donde tiene 2 métodos A y B (un constructor es principalmente un caso especial de un método) y A llama a B y B llama a A. Detectar un bucle infinito en la relación entre estos 2 métodos es es posible en el caso trivial (el que ha suministrado), pero resolverlo en general es similar a resolver el problema de la detención. Dado que resolver el problema de detención es imposible, los cumplidores generalmente no se molestan en intentarlo incluso en los casos simples.
Podría ser posible cubrir algunos casos simples utilizando un patrón FindBugs , pero no sería correcto para todos los casos.
No es necesariamente tan fácil como en tu ejemplo. Creo que resolver este problema sería igual a resolver el problema que se detiene, lo cual, como todos sabemos, es imposible.
Por favor, eche un vistazo a mi artículo en http://java.dzone.com/articles/tackling-circular-dependency
Creo que despejará tus dudas ...
Si realmente tiene un caso de uso como este, puede crear los objetos a pedido (perezosamente) y usar un captador:
public class B
{
private A a;
public B()
{
System.out.println("Creating B");
}
public A getA()
{
if (a == null)
a = new A();
return a;
}
}
(y de manera similar para la clase A
). Así que solo se crean los objetos necesarios si, por ejemplo, usted hace:
a.getB().getA().getB().getA()
Una solución similar para los captadores / definidores que utilizan la composición y la inyección del constructor para las dependencias. Lo importante a tener en cuenta es que los objetos no crean la instancia a las otras clases, se pasan (también conocido como inyección).
public interface A {}
public interface B {}
public class AProxy implements A {
private A delegate;
public void setDelegate(A a) {
delegate = a;
}
// Any implementation methods delegate to ''delegate''
// public void doStuff() { delegate.doStuff() }
}
public class AImpl implements A {
private final B b;
AImpl(B b) {
this.b = b;
}
}
public class BImpl implements B {
private final A a;
BImpl(A a) {
this.a = a;
}
}
public static void main(String[] args) {
A proxy = new AProxy();
B b = new BImpl(proxy);
A a = new AImpl(b);
proxy.setDelegate(a);
}