2015-01-10 2 views
10

В 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] 
    } 
} 
+13

Да, и именно поэтому мы стандарты кодирования, чтобы объявить имя переменных в верблюжьем, чтобы избежать этих проблем удобочитаемости. –

+4

@LuiggiMendoza ** нижний * camelCase. – Biffen

+0

"результат выполнения, ссылка на компилятор" Имя класса "как имя переменной." Это не правда. Это имя класса, а не имя переменной! –

ответ

14

Компилятор может определить контекст. В примере, вы дали:

ClassName ClassName = new ClassName(); 
    1   2    3 

Он может видеть, что где имя типа должно быть, поэтому он знает, что вы имеете в виду класс. Затем - это имя переменной, поэтому оно знает, что это должно быть имя переменной. И подходит после ключевого слова new с круглыми скобками, поэтому оно должно быть именем класса.

System.out.println(ClassName); 

В данном случае ClassName находится в контексте передачи аргументов. Имя типа не может быть передано в качестве аргумента, поэтому вы должны иметь в виду имя переменной.

Чтобы развлечь себя, вы можете изменить оператор печати на:

System.out.println(ClassName.class); 

Наведите курсор мыши на ClassName, и вы увидите, что компилятор распознает это как имя класса. Затем измените его на:

System.out.println(ClassName.getClass()); 

Наведите курсор еще раз, и теперь вы увидите, что он распознает его как имя переменной. Это потому, что .class может применяться только к имени типа, а getClass() может применяться только к ссылке на объект. Результат заявления печати будет одинаковым в обоих случаях, но с помощью разных механизмов.

Поэтому у компилятора нет проблем. Но вы правы, что это не читаемо людям. Соглашение состоит в том, что имена переменных и методов должны начинаться с строчной буквы, тогда как имена типов должны начинаться с буквы верхнего регистра. Соблюдение этого соглашения гарантирует, что такие проблемы с читабельностью не возникнут.

Я не могу точно сказать, почему авторы Java решили не применять это соглашение (т. Е. Дать ошибку компилятора, если имена типов начинались с строчной буквы или имен переменных/методов, начинающихся с прописных букв), но я предположите, что они не хотят делать что-либо фактической ошибкой, если это фактически не вызовет двусмысленности для компилятора . Ошибки компиляции должны указывать на проблему, которая делает компилятор неспособным выполнять свою работу.

+0

В случае, если вам интересно, почему два upvotes: https://stackoverflow.com/q/45108909/1541563 –

2

как можно составитель отличить "Classname"

Поскольку есть два компонента: Переменная типа и имя переменной. Вы объявляете переменную ClassName типа ClassName. Тип всегда идет первым. Классы не являются первоклассными объектами (что означает, что вы не можете иметь ссылку на класс), если вы не попадете в размышления (с свойством .class).

Поэтому в операторе печати:

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). Вероятно, один из:

  1. Не допускайте, чтобы статический и экземплярный метод имел одинаковое имя.
  2. Разрешите это, и при разрешении ссылки во время компиляции выберите метод экземпляра.
  3. Разрешите это, и при разрешении ссылки во время компиляции предпочитайте статический метод.

Если какой-либо из последних 2, я думаю, будет зарегистрировано предупреждение о компиляторе.

Теперь, когда вопрос компилятора в стороне, единственным другим потребителем кода являются люди. Компиляторы могут быть в состоянии полагаться на спецификации, чтобы гарантировать логическое поведение, но люди не могут. Мы смущаемся легко. Лучший совет, который у меня есть для этого, - просто, не делайте этого!

Нет абсолютно никакой причины указывать переменную, идентичную классу. На самом деле, большинство соглашений о стиле кодирования Java, которые я видел, используют lowerCamelCase для обозначения переменных и методов и UpperCamelCase для обозначения классов, поэтому нет возможности столкнуться, если вы не отклоняетесь от стандартов.

Если я столкнулся с кодом, подобным этому в проекте, над которым я работал, я бы сразу переименовал переменную, прежде чем делать что-либо еще.

Для моего неоднозначного случая экземпляра и статического метода с тем же именем может быть и урок для человека: не делайте этого!

В Java есть много правил, которые заставляют вас делать то, что логично, и сделать код легким для подражания, но в конце концов это все еще код, и вы можете написать любой код, который вы хотите. Никакая спецификация языка или компилятор не могут помешать вам писать путаный код.

+0

Я читаю [Java Spec. Глава 6: Имена] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html), но не может найти определенную часть, где это объясняется. Наверное, вы могли бы :) –

2
ClassName ClassName = new ClassName(); 

Если вы изучаете курс проектирования компилятора, вы узнаете, что есть шаг лексического анализа. На этом этапе вы будете писать грамматику для своего языка. например:

ClassName variableName = new 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(); понимаете как переменную - лучший способ в оформлении языка.

Надеется, что это поможет :)

Смежные вопросы