В Java имя переменной может быть одинаковым с именем класса
В Java я могу объявить переменную, имя которой совпадает с ее именем класса. Я думаю, что это такой запутанный и странный дизайн.
Итак, у меня есть проблема в фрагменте кода ниже: как компилятор может отличить ClassName
, он ссылается на имя переменной или класс?
В текущем результате компилятор ссылается на ClassName
как имя переменной.
class ClassName{}
public class Test {
public static void main(String[] args){
ClassName ClassName = new ClassName();
System.out.println(ClassName); //[email protected]
}
}
Ответы
Ответ 1
Компилятор может определить контекст. В примере, который вы указали:
ClassName ClassName = new ClassName();
1 2 3
Он может видеть, что 1 - это имя типа, поэтому он знает, что вы имеете в виду класс. Тогда 2 - это то, где ожидается имя переменной, поэтому оно знает, что это должно быть имя переменной. И 3 идет после ключевого слова new
с круглыми скобками, поэтому это должно быть имя класса.
System.out.println( ClassName );
В этом случае ClassName
находится в контексте передачи аргументов. Имя типа не может быть передано в качестве аргумента, поэтому вы должны иметь в виду имя переменной.
Чтобы развлечь себя, вы можете изменить оператор печати на:
System.out.println( ClassName.class );
Наведите указатель мыши на ClassName
, и вы увидите, что компилятор распознает это как имя класса. Затем измените его на:
System.out.println( ClassName.getClass() );
Наведите курсор еще раз, и теперь вы увидите, что он распознает его как имя переменной. Это потому, что .class
может применяться только к имени типа, а getClass()
может применяться только к ссылке на объект. Результат утверждения печати будет одинаковым в обоих случаях, но с помощью разных механизмов.
Итак, у компилятора нет проблем. Но вы правы, что это не читаемо для людей. Соглашение состоит в том, что имена переменных и методов должны начинаться с строчной буквы, тогда как имена типов должны начинаться с буквы верхнего регистра. Соблюдение этого соглашения гарантирует, что такие проблемы с читабельностью не возникнут.
Я не могу точно сказать, почему авторы Java предпочли не применять это соглашение (то есть дать ошибку компилятора, если имена типов начинались с строчных букв или имен переменных/методов, начинающихся с прописных букв), но я размышляю что они не хотят делать что-либо фактической ошибкой, если это фактически не вызовет двусмысленности для компилятора. Ошибки компиляции должны указывать на проблему, которая заставляет компилятор не выполнять свою работу.
Ответ 2
как компилятор может отличить "Имя класса"
Потому что есть два компонента: тип переменной и имя переменной. Вы объявляете переменную ClassName
типа ClassName
. Тип всегда идет первым. Классы не являются первоклассными объектами (что означает, что вы не можете иметь ссылку на класс), если вы не попадете в отражения (с свойством .class
).
Поэтому в инструкции print:
System.out.println(ClassName);
Это может быть только переменная. System.out.println
принимает ссылку на объект, и у вас есть объект, на который ссылается переменная с именем ClassName
, поэтому компилятор может ее решить.
Единственный случай, который может показаться неоднозначным для компилятора, заключается в том, что переменная относится к объекту, который имеет метод экземпляра с тем же именем, что и статический метод в классе.
public class SomeClass {
public void aMethod() {
System.out.println("A method!");
}
public static void aMethod() {
System.out.println("Static version!");
}
}
public class TestClass {
public static void main (String[] args) {
SomeClass SomeClass = new SomeClass();
SomeClass.aMethod(); // does this call the instance method or the static method?
}
}
Я уверен, что компилятор обнаружит неоднозначность и обработает ее определенным образом (в спецификации Java). Вероятно, один из:
- Не допускайте, чтобы статический и экземплярный метод имел одно и то же имя.
- Разрешить это, и при разрешении ссылки во время компиляции предпочитайте метод экземпляра.
- Разрешить это, и при разрешении ссылки во время компиляции предпочитайте статический метод.
Если какой-либо из последних 2, я думаю, будет зарегистрировано предупреждение компилятора.
Теперь, когда вопрос компилятора в стороне, единственным другим потребителем кода являются люди. Компиляторы могут быть в состоянии полагаться на спецификации, чтобы гарантировать логическое поведение, но люди не могут. Мы смущаемся легко. Лучший совет, который у меня есть, - просто, не делайте этого!
Нет абсолютно никаких оснований для обозначения переменной, идентичной классу. Фактически, большинство соглашений о стиле кодирования Java, которые я видел, используют lowerCamelCase для обозначения переменных и методов и UpperCamelCase для обозначения классов, поэтому нет возможности столкнуться, если вы не отклоняетесь от стандартов.
Если я столкнулся с таким кодом в проекте, над которым я работал, я бы сразу переименовал переменную, прежде чем делать что-либо еще.
Для моего двусмысленного случая экземпляра и статического метода с тем же именем, может быть, есть и человеческий урок: не делайте этого!
В Java есть много правил, которые заставляют вас делать то, что логично, и сделать код легким для подражания, но в конце дня он все еще кодируется, и вы можете написать любой код, который вы хотите. Никакая спецификация языка или компилятор не могут помешать вам писать путающий код.
Ответ 3
ClassName ClassName = new ClassName();
Если вы изучаете курс проектирования компилятора, вы узнаете, что есть шаг лексического анализа. На этом этапе вы будете писать грамматику для своего языка. например:
ClassName variableName = new ClassName();
Итак, пример выше, компилятор может понять, что второй ClassName является переменной.
Когда вы делаете что-то вроде:
ClassName.doSomething();
Java будет понимать ClassName как переменную, а не класс. И этот дизайн не будет иметь никаких ограничений. doSomething()
может быть как статическим методом, так и просто методом экземпляра.
Если Java понимает ClassName здесь как класс, поэтому doSomething()
не может быть методом экземпляра. Может быть, потому, что этот создатель Java выбрал выше дизайна: ClassName как переменная.
Но какая проблема, если имя переменной не может быть тем же именем с их классом. поэтому следующий пример:
ClassA ClassB = new ClassA();
ClassB.callMethodInClassB(); // should compile error or not ???!!!
Проблема все еще здесь. До сих пор существует заблуждение. Таким образом, новый дизайн должен быть:
No variable name should not has same name with **any** class name.
И вы увидите, что это утверждение делает один язык более сложным, чтобы понять и не так хорошо определить. Из вышеприведенных доказательств я думаю, что когда вы делаете что-то вроде: A A = new A();
понимаете, что A как переменная - лучший способ в дизайне языка.
Надеюсь, что эта помощь:)