我已经定义了以下Java类:
mac-grek:javajunk grek$ cat A\$B.java
class A$B {}
mac-grek:javajunk grek$ cat A.java
public class A {
public static class B {}
}
mac-grek:javajunk grek$ cat Main.java
public class Main {
public static void main(String[] args) {
System.out.println(A.B.class.getName());
System.out.println(A$B.class.getName());
}
}
当我尝试编译它们时,我会遇到以下错误:
mac-grek:javajunk grek$ javac 'A$B.java' A.java Main.java
A.java:2: duplicate class: A.B
public static class B {}
^
Main.java:4: cannot find symbol
symbol : class B
location: class A
System.out.println(A.B.class.getName());
^
Main.java:5: cannot find symbol
symbol : class A$B
location: class Main
System.out.println(A$B.class.getName());
^
3 errors
如果我从A.java
删除System.out.println(A.B.class.getName());
文件和Main.java
,则所有内容都会编译:
mac-grek:javajunk grek$ cat A\$B.java
class A$B {}
mac-grek:javajunk grek$ cat Main.java
public class Main {
public static void main(String[] args) {
System.out.println(A$B.class.getName());
}
}
mac-grek:javajunk grek$ javac A\$B.java Main.java
mac-grek:javajunk grek$
所以Java允许我定义一个包含美元符号的类。我该如何编译原始示例?
答案 0 :(得分:34)
您有一个名称冲突,因为您定义了一个顶级类A $ B,其名称与A类的静态内部类B的生成名称相同。由于您同时具有这两者,编译器无法解决冲突。
The JLS说:
$字符只能用于机械生成的源代码,或者很少用于访问旧系统上预先存在的名称。
既然你决定不尊重这条规则,你就被javac咬了。我只想将A $ B重命名为其他东西。
答案 1 :(得分:12)
这条规则很模糊。
我不同意。对我而言,它说“不要这样做......除非你知道你在做什么”。它没有说明原因,但它不需要。实际上,无法完全解释原因,因为标识符中'$'
的某些用法可能来自第三方软件。
为什么javac会关心我的代码是来自某个生成器还是手写?
它不“关心”。但另一方面,如果您选择忽略JLS建议,它会假设您知道自己在做什么。
重点是编写生成器的人应该知道内部类的二进制类名称是使用'$'
字符表示的。其他人应该只遵循JLS中的建议。
Java语言规范没有说明JVM如何使用'$'
的原因是“关注点分离”。从JLS的角度来看,这只是一个实现细节。实际上,可以想象有人会为虚拟机平台实现Java语言,以不同的方式处理内部类。
我想知道在哪些情况下我可以在我的课程名称中使用$。
最终无法回答这个问题。它是不可能的,这可能是一件好事;见下文。
JLS应该非常精确和正式,在解释它的陈述时不应该提到读者的心理状态。
在某些方面,JLS的正式和精确是不好的。这是其中之一。例如,如果他们声明'$'可以安全地遵循规则X,Y和Z,这将限制他们的在未来版本的Java中可能使用'$'。更改有关哪些标识符可行的规则可能会导致主要的源代码兼容性问题。
(这个原则适用的其他领域是内存模型和垃圾收集的语义。)
JLS不涉及您的精神状态。那些是我的字样。
答案 2 :(得分:2)
您的班级class A$B
和您的static class B
共享相同的内容。编译器生成OuterClassName$InnerClassName
作为嵌套类的类名