简短回答:大多数程序员对作用域的心智模型并不是javac使用的模型。采用更直观的模型需要对javac进行重大改变。
内部类中静态成员变量最主要的原因是为了使代码更清晰——一个只被内部类使用的静态成员变量应该存在于内部类中,而不是放置在外部类中。请考虑以下示例:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
考虑一下在getID()中使用不合格标识符“outID”时发生了什么。出现此标识符的范围应该是这样的:
Outer -> Inner -> getID()
这里再次强调,由于javac编译器的工作原理,"Outer"级别的作用域包括Outer类的静态和实例成员。这很令人困惑,因为通常我们会认为类的静态部分是作用域的另一个级别:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
以这种思考方式,当然staticMethod()只能看到Outer的静态成员。但如果这就是javac的工作方式,那么在静态方法中引用实例变量将导致“名称无法解析”的错误。实际发生的是该名称在范围内被找到,但随后会出现额外的检查级别,并确定该名称是在实例上下文中声明的,并且正在从静态上下文中引用。
好的,这与内部类有什么关系呢?我们天真地认为内部类没有静态范围的原因是因为我们想象范围像这样工作:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
换句话说,内部类中的静态声明和外部类中的实例声明都在内部类的实例上下文中范围内,但两者都不是相互嵌套的;而是都嵌套在Outer的静态范围中。
这就不是javac的工作方式 - 静态成员和实例成员有一个单独的作用域级别,并且作用域始终严格嵌套。即使继承也是通过将声明复制到子类中来实现的,而不是通过分支和搜索超类作用域来实现的。
为了支持内部类的静态成员,javac必须将静态和实例作用域拆分并支持分支和重新加入作用域层次结构,或者它必须扩展其简单的布尔“静态上下文”概念以更改跟踪当前作用域中所有嵌套类的上下文类型。