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

时间:2010-08-01 19:40:06

标签: java

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

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

2 个答案:

答案 0 :(得分:20)

  

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

我能够将片段剪切为以下内容:

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

这个场景在Java Puzzlers中讨论,益智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的默认构造函数非法,因为它需要将一个封闭的实例提供给构造函数

解决问题的“强力”方法是明确地提供一个合格的 - this表达式:

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

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

  

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

     

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

答案 1 :(得分:0)

不确定目标是什么,但试试这个。注意我还将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()

希望这有帮助!