Имя класса 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(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 позволяет мне определить класс, содержащий знак доллара в его имени. Как я могу скомпилировать мой оригинальный пример?
3 ответа
У вас конфликт имен, потому что вы определили класс верхнего уровня A$B, имеющий то же имя, что и сгенерированное имя для статического внутреннего класса B класса A. Поскольку у вас есть оба, компилятор не может разрешить конфликт.
JLS говорит:
Символ $ следует использовать только в механически сгенерированном исходном коде или редко для доступа к уже существующим именам в устаревших системах.
Поскольку вы решили не соблюдать это правило, вас укусил Джавак. Я бы просто переименовал A$B во что-то другое.
Это правило очень расплывчато.
Я не согласен. Для меня это говорит "не делай этого... если ты не знаешь, что делаешь". Это не говорит почему, но это не нужно. В самом деле, это не может полностью объяснить, почему, потому что некоторые из '$'
в идентификаторы могут входить сторонние программы.
Почему javac будет беспокоиться, если мой код исходит от какого-то генератора или написан вручную?
Это не "все равно". Но с другой стороны, предполагается, что вы знаете, что делаете, если решите игнорировать рекомендации JLS.
Дело в том, что люди, которые пишут генераторы, должны знать, что имена двоичных классов для внутренних классов представлены с использованием '$'
персонаж. Другие люди должны просто следовать советам в JLS.
Причина, по которой в спецификации языка Java не указано, как JVM использует '$'
это "разделение интересов". С точки зрения JLS, это просто деталь реализации. Действительно, возможно, что кто-то реализует язык Java для платформы виртуальной машины, которая по-разному обрабатывает внутренние классы.
Я хотел бы точно знать, в каких случаях я могу использовать $ в названиях моих классов.
На это невозможно ответить однозначно. И, наверное, хорошо, что это невозможно; увидеть ниже.
JLS должен быть очень точным и формальным и не должен ссылаться на психическое состояние читателя при интерпретации его утверждений.
Есть некоторые области, где было бы плохо для JLS быть формальным и точным. Это одна из них. Например, если бы они объявили, что "$" может безопасно, если вы следуете правилам X, Y и Z, это ограничит их возможное использование "$" в будущих версиях Java. Изменение правил о том, какие идентификаторы работоспособны, может вызвать серьезные проблемы с совместимостью исходного кода.
(Другими областями применения этого принципа являются Модель памяти и семантика сбора мусора.)
JLS не относится к вашему психическому состоянию. Это были мои слова.
Как ваш класс class A$B
и ваш static class B
разделяет то же самое. Компилятор генерирует OuterClassName$InnerClassName
как имя класса для вложенных классов