Situación extraña para “no se puede hacer referencia a esto antes de que se haya llamado al constructor de supertipo”

¿Por qué este código no se comstack?

public class A { public class B extends A { public B(A a) { } } void foo() { A a = new A(); new B(a) { }; } } 

A.java:[7,17] cannot reference this before supertype constructor has been called

La comstackción es exitosa si se realizan cualquiera de estos cambios:

  • B es privado en lugar de público
  • la línea 7 lee el new B(A); en lugar de new B(A) { }

Usando la versión de javac: 1.6.0_20

Cabe señalar que Eclipse, javac e Intellij IDEA muestran diferencias en los comportamientos con respecto a estos fragmentos. El comportamiento de Java Puzzlers y javac se usa como referencia en esta discusión.

Pude reducir el fragmento a lo siguiente:

 public class A { class B extends A { } void foo() { new B() { }; // DOES NOT COMPILE!! } } 

Este escenario es discutido en Java Puzzlers , Puzzle 90: ¡Es absurdo, es un dolor, es superclase!

El fragmento dado es el siguiente:

 public class Outer { // "A" class Inner1 extends Outer {} // "B" class Inner2 extends Inner1 {} // "B" anonymous } // DOES NOT COMPILE!! 

El problema es que debido a cómo se define el constructor predeterminado, realmente tenemos lo siguiente:

 // Same as above but with default constructor included explicitly public class Outer { class Inner1 extends Outer { Inner1() { super(); } } class Inner2 extends Inner1 { Inner2() { super(); } } } // STILL DOES NOT COMPILE!! 

El problema es que la superclase de Inner2 es en sí misma una clase interna Inner1 , lo que Inner2 el constructor predeterminado de Inner2 ilegal, ya que requiere que se suministre una instancia adjunta al constructor.

La forma de “fuerza bruta” para solucionar el problema es proporcionar esto explícitamente con una expresión calificada:

 // "brute-force" fix public class Outer { class Inner1 extends Outer { Inner1() { super(); } } class Inner2 extends Inner1 { Inner2() { Outer.this.super(); } } } // NOW COMPILES! 

Sin embargo, el acertijo prescribe que una situación tan complicada es mejor evitarla en primer lugar. Aquí hay algunas citas:

Esto comstack, pero es tremendamente complejo. Hay una solución mejor: cada vez que escriba una clase para miembros, pregúntese: ¿esta clase realmente necesita una instancia adjunta? Si la respuesta es no, hágalo static . Las clases internas a veces son útiles, pero pueden fácilmente presentar complicaciones que dificultan la comprensión de un progtwig. Tienen interacciones complejas con generics (Puzzle 89), reflexión (Puzzle 80) y herencia (este rompecabezas). Si declara que Inner1 es static , el problema desaparece. Si también declaras que Inner2 es static , realmente puedes entender lo que hace el progtwig: una buena bonificación de hecho.

En resumen, rara vez es apropiado que una clase sea tanto una clase interna como una subclase de otra. De manera más general, raramente es apropiado extender una clase interna; si es necesario, piense detenidamente sobre la instancia adjunta. Además, prefiere clases anidadas static a no static . La mayoría de las clases miembro pueden y deben ser declaradas static .

No estoy seguro de cuál es el objective exactamente, pero intente esto. Tenga en cuenta que también recorté el paso de A como argumento ya que las clases no estáticas ya están vinculadas. Incluí la syntax para referirme a la clase externa “esto” para aquellas situaciones donde la clase interna puede encubrir un campo / método de clase externo

 public class A { protected String field = "a"; public class B extends A { protected String field = "b"; public B() { System.out.println("" + A.this.field + " " + this.field); } @Override void foo() { System.out.println("b.foo()"); } } void foo() { A a = new A(); a.field = "a2"; B b = a.new B() { @Override void foo() { System.out.println("b.anon.foo()"); } }; b.foo(); } public static void main(String[] args) { new A().foo(); } } 

Al ejecutar esto se generará:

 a2 b b.anon.foo() 

¡Espero que esto ayude!