奇怪的情况“在超类型构造函数被调用之前无法引用它”

为什么这段代码不能编译?

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

如果进行以下任一更改,则编译成功:

  • B是私人而非公开
  • 第7行读new B(A); 而不是new B(A) { }

使用javac版本:1.6.0_20

应该注意的是,Eclipse, javac和Intellij IDEA在这些片段方面表现出不同的行为。 javacJava Puzzlers行为在本讨论中用作参考。

我能够将片段减少到以下内容:

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

这个场景在Java PuzzlersPuzzle 90中讨论过:它是荒谬的,它是一个痛苦,它是超类!

给出的片段如下:

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

问题是由于定义了默认构造函数,我们确实有以下内容:

 // 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!! 

问题是Inner2的超类本身就是一个内部类Inner1 ,因此它使得Inner2的默认构造函数非法,因为它需要将一个封闭的实例提供给构造函数。

解决问题的“powershell”方法是明确地为此提供一个限定的表达式:

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

然而,这个谜题规定,首先要避免这种复杂的情况。 以下是一些引用:

这编译,但它是令人头脑麻木的复杂。 有一个更好的解决方案:每当你写一个成员类时,问问自己,这个类真的需要一个封闭的实例吗? 如果答案是否定的,请将其static 。 内部类有时是有用的,但它们很容易引入使程序难以理解的复杂性。 它们与generics(Puzzle 89),reflection(Puzzle 80)和inheritance(这个难题)有着复杂的相互作用。 如果将Inner1声明为static ,则问题就会消失。 如果你还宣称Inner2static ,你实际上可以理解程序的作用:确实是一个很好的奖励。

总之,一个类很少适合作为另一个类的内部类和子类。 更一般地说,扩展内部阶级很少是合适的; 如果必须,请仔细思考封闭的实例。 此外,首选static嵌套类为非static类。 大多数成员类可以而且应该被声明为static

不确定目标是什么,但试试这个。 注意我还将A作为参数的传递剪切为它们已经链接的非静态类。 我包含了引用外部类“this”的语法,因为内部类可能会掩盖外部类字段/方法

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

运行此将输出:

 a2 b b.anon.foo() 

希望这可以帮助!