[英]“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 不是file1
和file2
的内部 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()
方法是完全合法的,因为Inner
与file1
的特定实例相关联,因此它可以访问特定于实例的(非静态)字段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
的特定实例。
将内部 class 更改为嵌套的 class。 如果您不需要从嵌套的 class 访问特定于实例的数据,那么这是通往 go 的方法。 也许您有一辆汽车 class,并且您想要一个嵌套的 class 用于车轮。 如果我们在汽车零件商店的意义上谈论这个,车轮没有连接到任何汽车,所以我们不希望我们的嵌套 class 连接到外部 class 的任何实例。 只需像这样修改file1
:
public class file1 {
// Added static keyword:
protected static class Nested {
public int x = 8;
}
}
使子类扩展超类的特定实例的内部 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);
}
}
}
内部类不同于嵌套类。 内部类绑定到外部 class 的特定实例,而嵌套类不绑定到任何特定实例。 您不能在不指定实例的情况下引用内部 class,因为这与内部 class 的定义背道而驰。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.