为什么这段代码甚至无法编译?
public class T {
public static void main(String[] args) {
class A extends B {}
class B {}
B a = new A();
}
}
错误:
Error:(10, 25) java: cannot find symbol
symbol: class B
location: class com.test.T
Error:(12, 15) java: incompatible types
required: B
found: A
在声明此类时,顺序真的重要吗?
答案 0 :(得分:9)
是的,本地课程的非常重要。值得注意的是,本地类在实际代码中难以置信罕见。我只记得永远使用一次。但是,为了感兴趣......
来自JLS,section 6.3:
由块(第14.2节)直接包含的本地类声明的范围是直接封闭块的其余部分,包括它自己的类声明。
现在“休息”并不十分清楚,但我相信这意味着“从这一点开始”。所以基本上B
不在A
的声明中,因此错误。
为了增加乐趣,在声明B
之前,您可以引用名为B
的不同类型:
public class T {
public static void main(String[] args) {
class A extends B {}
class B {}
B a = new A();
}
}
class B {}
给出:
错误:不兼容的类型:A无法转换为B
答案 1 :(得分:1)
对于顶级课程,没有。
对于本地课程,是的,但请参阅John Skeet对此的回答:https://stackoverflow.com/a/32254520/257090
Java与C#以及大多数其他现代编译器一样,首先解析整个文件,以及同一范围内的其他文件或导入,以使用它看到的顶级类型符号填充编译器符号表(无在此步骤中进一步解析这些类型的内部结构,然后在后面的步骤中,编译器解析符号引用;一旦收集了所有符号,它们就没有词汇顺序或优先级,就像它们同时在同一行上声明一样。这就是它如何实现以下概念(来自JLS - http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.3)
声称声明属于a中特定点的范围 当且仅当声明的范围包括该点时才编程。
可观察(第7.4.3节)顶级声明的范围 package是所有可观察的编译单元(第7.3节)。
顶级类型(第7.6节)的范围是所有类型声明 声明顶级类型的包。
等等......
这就是为什么在Java / C#中创建循环关系很简单,而没有像C / C ++这样的旧语言所需的前向声明,其中编译器只看起来“向后”,所以前向声明是一个虚拟或占位符真是前进。
但是,在Java方法体中,Java使用词法顺序,并使用与局部变量相同的排序规则来处理类。 (参见John Skeet的回答:https://stackoverflow.com/a/32254520/257090)
我可以想到为什么这对于理智而言必须如此,但它归结为编译器/语言设计者做出的决定。我的意见是,它只是为了保持一致性。否则,我们对本地事物有两种不同的规则,这种事情会让人感到困惑。如果您曾经做过很多Perl编程,那么您就知道维护特殊情况和特定于上下文的规则的心理缓存是多么具有挑战性。
答案 2 :(得分:-2)
改变这个:
公共类T {
public static void main(String[] args) {
B b = new A();
}
}
B班{ }
A类延伸B { }