繁体   English   中英

“范围内没有 file1 类型的封闭实例”内部类错误

[英]“no enclosing instance of type file1 is in scope” error with inner classes

我有一些代码(本质上它导入了另一个 class ,它有一个我正在尝试访问的内部 class ),但它带来了这个错误:

file2.java:5: error: no enclosing instance of type file1 is in scope
    public static class file3 extends file2.Inner {

“范围内没有 file1 类型的封闭实例”是什么意思?

这是代码:

package test;
import secret.file1;

public class file2 extends file1 {
    public static class file3 extends file2.Inner {
        public static void main(String[] args) {
            file3 outer = new file3();
            System.out.println(outer.x);
        }
    }
}

file1的代码:

package secret;

public class file1 {
    protected class Inner {
        public int x = 8;
    }
}

仅用于教育目的,请勿在实践中使用。

内部类具有这种独特的行为,其中所有构造函数都在第一个 position 中隐式声明封闭类型的参数。 所以,

public class file1 {
    public class Inner {
    }
}

实际上被编译成类似的东西

public class file1 {
    public class Inner {
        public Inner(file1 enclosing) {
        }
    }
}

其中enclosing实际上是对其初始化的封闭Outer实例的引用。

new file1().new Inner();
// compiles to something like
file1 enclosing = new file1();
new Inner(enclosing);

请记住,对于常规 inheritance,如果超类声明了参数化构造函数(并且没有无参数构造函数),则子类必须使用super(...)在自己的构造函数中调用它。 使用内部类的 inheritance,如果还隐式提供了封闭实例,则可以隐式执行super(...)调用。

也就是说,如果file3实际上也是file1 (或file2 )的内部 class,而不是嵌套的 class,则您的 class 层次结构将被编译。

public class file2 extends file1 {
    public class file3 extends file2.Inner {
    }
}

正如我之前所说, file3将编译为类似

public class file3 extends file2.Inner {
    public file3(file2 enclosing) {
        super(enclosing); // this is calling the `public Inner(file1)` constructor
    }
}

super(..)调用将隐式完成,因为“ file1类型的封闭实例在范围内”。

但是,因为您的file3 class 不是file1file2的内部 class ,所以没有隐式封闭file1实例传递给超级构造函数,因此 ZD52387880E1EA22817A72D3759213819 编译器会产生错误。

我一直隐含地加粗这个词,试图强调 Java 让你明确地绕过这一切。 此功能是内部类独有的。 Java 提供以下语法来声明带有参数(在任何位置)的构造函数,以显式视为封闭实例

public static class file3 extends file2.Inner {
    public file3(file2 enclosing) {
        enclosing.super(); // special syntax
    }
    // [...]
}

而且,就像常规的 inheritance 一样, super(..)调用必须是构造函数主体中的第一条语句。

然后你的main方法就变成了

file2 file2 = new file2();
file3 outer = new file3(file2);
System.out.println(outer.x);

您在其中明确提供file2.Inner所需的封闭实例(也可以通过null )。


供您参考,还有显式声明我在第二个片段中提到的构造函数的语法,其中参数表示封闭实例

public class file1 {
    public class Inner {
        public Inner(@SomeAnnotation file1 file1.this) {
        }
    }
}

这仅在您要注释参数时才真正有用。

此信息分布在Java 语言规范的第 8 章中,特别是关于“内部类”和“接收器参数”的部分。

问题

您正在尝试使嵌套的 class 成为内部 class 的子类,而这实际上是不可能的。 有一种方法可以做到这一点,请参阅 Savior 的语法答案,但在您的情况下,它对您没有帮助。 The key difference between a nested class and an inner class is that an inner class is tied to a specific instance of the outer class, whereas a nested class isn't tied to any particular instance. 为了说明这种差异,让我们稍微修改一下您的代码:

public class file1 {
    private int x;

    public file1(int x) {
        this.x = x;
    }

    // No static keyword, so this is an inner class:
    protected class Inner {
        public void printXInner() {
            // This is legal:
            System.out.println(x);
        }
    }

    // Has static keyword, so this is a nested class:
    protected static class Nested {
        public void printXNested() {
            // This is NOT legal:
            System.out.println(x);
        }
    }
}

printXInner()方法是完全合法的,因为Innerfile1的特定实例相关联,因此它可以访问特定于实例的(非静态)字段x 但是, printXNested()方法会抛出编译时异常,因为它不知道您要使用哪个x 您可能有很多file1实例,并且每个实例的x值可能不同。

现在,为什么这很重要,它与您的代码有什么关系? 好吧,假设 Java 刚刚决定允许您尝试做的事情; 您可以扩展内部类。 如果我们仍然使用上面修改过的代码,我们可以这样写:

public class file2 extends file1.Inner {
    public void printXFile2() {
        printXInner(); // Uh oh, what should the program do here?
    }
}

这段代码看起来完全无害,它扩展了Inner并调用了printXInner()方法。 除了我们的老问题再次出现:它应该打印哪个x 我们还没有指定使用哪个file1实例。 因此,您实际上是在尝试在 static 上下文中使用内部 class 。


现在,希望你在想“好吧,我明白为什么会出现问题,但我该如何解决呢?” 你基本上有两个选择:你可以将你的内部 class 更改为嵌套的 class,你可以将你的文件 2 file2更改为内部 ZABC2ABB12F2A29FDCC40。 您选择哪个选项将取决于您的代码结构。 您需要决定是否要将Inner绑定到file1的特定实例。

修复 #1

将内部 class 更改为嵌套的 class。 如果您不需要从嵌套的 class 访问特定于实例的数据,那么这是通往 go 的方法。 也许您有一辆汽车 class,并且您想要一个嵌套的 class 用于车轮。 如果我们在汽车零件商店的意义上谈论这个,车轮没有连接到任何汽车,所以我们不希望我们的嵌套 class 连接到外部 class 的任何实例。 只需像这样修改file1

public class file1 {
    // Added static keyword:
    protected static class Nested {
        public int x = 8;
    }
}

修复 #2

使子类扩展超类的特定实例的内部 class。 如果您需要从内部 class 访问特定于实例的数据,这是通往 go 的方法。 也许您有一辆汽车 class,并且您想为车轮配备一个内部 class。 如果我们在驾驶模拟器的意义上谈论这个,那么车轮连接到特定的汽车上并且它们保持连接状态。 因此,我们希望我们的内部 class 附加到外部 class 的特定实例。 只需像这样修改file3

public class file2 extends file1 {
    // Removed static keyword:
    public class file3 extends Inner { ... }
}

不过,这可能会产生次要问题。 内部 class 中不能有 main 方法,因为在启动时,没有外部 class 的实例,因此 JRE 无法调用内部 class 的方法。 一种解决方法是将 main 方法放在外部 class 中,然后从那里调用特定实例上的方法。 你可以这样实现:

public class file2 extends file1 {
    public static void main(String[] args) {
        new file2().file3.doSomething();
    }

    public class file3 extends Inner {
        public static void doSomething() {
            file3 instance = new file3();
            System.out.println(instance.x);
        }
    }
}

TL;博士

内部类不同于嵌套类。 内部类绑定到外部 class 的特定实例,而嵌套类不绑定到任何特定实例。 您不能在不指定实例的情况下引用内部 class,因为这与内部 class 的定义背道而驰。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM