当前位置: 首页 > 面试题库 >

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

龚博涛
2023-03-14
问题内容

为什么此代码无法编译?

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的谜题
行为被用于讨论参考。

我可以将代码段缩减为以下内容:

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

Java Puzzlers
Puzzle 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。内部类有时很有用,但是它们很容易引入复杂性,使程序难以理解。它们与泛型(难题89),反射(难题80)和继承(此难题)具有复杂的交互。如果您声明Inner1static,问题就解决了。如果您还声明Inner2static,则实际上可以理解该程序的功能:确实是一个不错的奖励。

总而言之,很少将一个类同时用作另一个的内部类和子类。更普遍地讲,扩展内部类很少是合适的。如果需要,请仔细考虑该封闭实例。此外,static与non-
相比,更喜欢嵌套static。可以并且应该声明大多数成员类static



 类似资料:
  • 问题内容: 考虑以下Java类声明: 该代码将无法编译,编译器会抱怨上面我突出显示的那一行。为什么会发生此错误,最好的解决方法是什么? 问题答案: 之所以无法最初编译代码,是因为该类是一个 实例变量,这意味着在创建类型的对象时,还将创建的唯一实例并将其附加到该特定对象。因此,无法在构造函数中进行引用,因为尚未创建引用,也没有创建对象。 解决方案是使最终变量: 通过创建变量,它变得与类本身相关联,而

  • 问题内容: 我有一个类A,并写了一个子类B。A只有一个参数化的构造函数。B必须调用A的这个超级构造函数。现在,我想使用一个Object作为参数。该对象应调用B的方法。因此,参数对象必须持有B的引用,或者必须是内部类。 现在,当我想调用构造函数时,……Eclipse说: 在显式调用构造函数时无法引用“ this”或“ super” 我唯一想解决的是设置方法,将“ this”实例注入到参数对象中。我不

  • 为什么第一个输出有柱线 = 空?是因为在创建类 B 之前调用了 B.foo() 吗?如果是,那么为什么可以调用 B.foo()?还是因为 B.foo() 中的字段栏试图从 A 获取柱线字段但无法访问它? 我的问题与链接的问题不同,我不是在问调用顺序,我是在问为什么第一个输出为空?另一个问题不是关于字段或空变量。 我不明白B.foo中的bar变量是如何为空的,如果它是在A和B中定义的。

  • 问题内容: 如果我有一个像这样的抽象类: 还有一些从Item派生的类是这样的: 我不明白为什么我不能使用泛型调用构造函数: 我知道可以有一个没有构造函数的类型,但是这种情况是不可能的,因为Pencil具有没有参数的构造函数,而Item是抽象的。但是我从eclipse中得到了这个错误: 无法实例化 我不明白为什么的 T类型 ,以及如何避免这种情况? 问题答案: 无法使用Java类型系统来强制类层次结

  • 例如, 这将引入编译错误“调用构造函数时无法引用实例字段”。 StackOverflow中有关此场景的类似问题的一些答案转到“因为当前实例仍在构建中”或“堆中尚未创建的实例”。 然而,令人困惑的是,在超类构造函数中,可以调用可重写的成员方法,这些方法可能访问子类当前实例的字段。在Java也没问题。 我的问题是 > 在实例构造过程中,子类的字段从哪一刻开始可以引用? “在调用构造函数时不能引用实例字

  • 我正在做一个项目(Next.js 8.1.0版),我想升级到9.2版。接下来我换了衣服。js版本到9.2,我遇到了这个问题: 类型错误:类构造函数应用程序不能被调用没有新的在新的MyApp(/home/节点/应用程序/src/. Next/服务器/静态/开发/页面/_app.js:4384: 191)在处理孩子(/home/节点/应用程序/node_modules/react-dom/cjs/re