如果存在内部类,则包含美元符号的Java类名无法编译

我已经定义了以下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(ABclass.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: AB public static class B {} ^ Main.java:4: cannot find symbol symbol : class B location: class A System.out.println(ABclass.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(ABclass.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允许我定义一个包含美元符号的类。 我该如何编译原始示例?

您有一个名称冲突,因为您定义了一个顶级类A $ B,其名称与A类的静态内部类B的生成名称相同。由于您同时具有这两个类,因此编译器无法解决冲突。

JLS说:

$字符只能用于机械生成的源代码,或者很少用于访问遗留系统上预先存在的名称。

既然你决定不尊重这条规则,你就被javac咬了。 我只想将A $ B重命名为其他东西。

这个规则很模糊。

我不同意。 对我来说,它说“不要这样做……除非你知道你在做什么”。 它没有说明原因,但它不需要。 实际上,它无法完全解释为什么,因为标识符中'$'某些用法可能来自第三方软件。

为什么javac会关心我的代码来自某个生成器还是手写?

它不“关心”。 但另一方面,如果您选择忽略JLS建议,它会假设您知道自己在做什么。

关键是编写生成器的人应该知道内部类的二进制类名称是使用'$'字符表示的。 其他人应该只遵循JLS中的建议。

Java语言规范没有说明JVM如何使用'$'是“关注点分离”。 从JLS的角度来看,这只是一个实现细节。 实际上,可以想象有人会为虚拟机平台实现Java语言,以不同方式处理内部类。


我想知道在哪些情况下我可以在我的课程名称中使用$。

最终无法回答这个问题。 它是不可能的,这可能是一件好事; 见下文。

JLS应该非常精确和正式,在解释它的陈述时不应该提到读者的心理状态。

  1. 在某些方面,JLS的正式和精确是不好的。 这是其中之一。 例如,如果他们声明’$’可以安全地遵循规则X,Y和Z,这将限制他们在未来版本的Java中可能使用’$’。 更改有关哪些标识符可行的规则可能会导致主要的源代码兼容性问题。

    (这个原则适用的其他领域是内存模型和垃圾收集的语义。)

  2. JLS不涉及您的精神状态。 那是我的话。

你的class级class A$B和你的static class B都是一样的。 编译器生成OuterClassName$InnerClassName作为嵌套类的类名