17

In Java I can declare a variable, whose name is total same with its classname. I think it is a so confusing and strange design.

So I have a problem in the code snippet below: how can the compiler distinguish the ClassName, it is referenced the variable name or class name?

In the running result, the compiler references ClassName as a variable name.

class ClassName{}

public class Test {
    public static void main(String[] args){
        ClassName ClassName = new ClassName();
        System.out.println(ClassName); //ClassName@18fb53f6
    }
}
John Topley
  • 113,588
  • 46
  • 195
  • 237
Jimmy Zhang
  • 939
  • 1
  • 10
  • 15
  • 4
    @LuiggiMendoza **lower* camelCase. – Biffen Jan 10 '15 at 15:38
  • "running result, the compiler reference the "Classname" as a variable name." That's not true. It's the class name, not the variable name! – Kuba Spatny Jan 10 '15 at 15:39
  • "I think it is a so confusing and strange design" - @LuiggiMendoza has already answered that for you. Also, you have to remember that you can always use _ in combination with numbers if it helps matters become simpler. – ha9u63a7 Jan 10 '15 at 15:41
  • Related (a little) http://stackoverflow.com/q/15648312/1393766 :) – Pshemo Jan 10 '15 at 15:46

3 Answers3

22

The compiler can tell by context. In the example you have given:

ClassName ClassName = new ClassName();
    1         2               3

It can see that 1 is where a type name should be, so it knows you mean the class. Then, 2 is where a variable name is expected, so it knows that this should be the name of a variable. And 3 is coming after the new keyword with parentheses, so it must be the name of a class.

System.out.println( ClassName );

In this instance, ClassName is in the context of argument passing. A type name can't be passed as an argument, so you must mean the name of the variable.

To amuse yourself, you can change the print statement to:

System.out.println( ClassName.class );

Hover your mouse cursor on ClassName and you'll see that the compiler recognizes this as the name of a class. Then change it to:

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

Hover your cursor again, and now you see that it recognizes it as the variable name. That's because .class can only be applied to a type name, while getClass() can only be applied to an object reference. The result of the print statement would be the same in both cases - but through different mechanisms.

So the compiler has no problem here. But you are right that it's not readable to humans. The convention is that names of variables and methods must start with a lowercase letter, while type names must start with an uppercase letter. Adhering to this convention will ensure that no such readability problems arise.

I can't say exactly why the authors of Java chose not to enforce this convention (that is, give a compiler error if type names started with a lowercase letter or variable/method names started with an uppercase), but I speculate that they didn't want to make anything an actual error unless it would actually cause an ambiguity for the compiler. Compilation errors are supposed to indicate a problem that makes the compiler unable to do its work.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
3

how can the compiler distinguish the "Classname"

Because there are two components: The variable type and variable name. You declare a variable ClassName of type ClassName. Type always goes first. Classes are not first-class objects (meaning you can't have a reference to a class) unless you get into reflections (with the .class property).

Therefore, in the print statement:

System.out.println(ClassName);

That can only be the variable. System.out.println takes an object reference, and you have an object referred to by a variable named ClassName, therefore the compiler can resolve it.

The only case I can think that is ambiguous to the compiler is if the variable refers to an object which has an instance method of the same name as a static method on the class.

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?
    }
}

I am sure the compiler will detect the ambiguity and handle it in some specified manner (in the Java spec). Probably one of:

  1. Don't allow a static and instance method to have the same name.
  2. Allow it, and when resolving the reference at compile-time, prefer the instance method.
  3. Allow it, and when resolving the reference at compile-time, prefer the static method.

If either of the last 2, I imagine a compiler warning would be logged.

Now that the compiler question is aside, the only other consumer of the code is human beings. Compilers may be able to rely on specifications to guarantee rationale behavior, but humans can't. We get confused easily. The best advice I have for that is simply, don't do it!

There is absolutely no reason to name a variable identically to a class. In fact, most Java coding style conventions I have seen use lowerCamelCase to name variables and methods and UpperCamelCase to name classes, so there is no way for them to collide unless you deviated from the standards.

If I encountered code like that in a project I was working on, I would immediately rename the variable before doing anything else.

For my ambiguous case of an instance and static method of the same name, there just might be a human lesson in there too: don't do it!

Java has a lot of rules to force you to do things that are logical and make code easy to follow, but at the end of the day, it's still code and you can write any code you want. No language spec or compiler can prevent you from writing confusing code.

Brandon
  • 9,822
  • 3
  • 27
  • 37
  • I'm reading the [Java Spec. Chapter 6: Names](http://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html) but cannot find a specific part where this is explained. Probably you could :) – Luiggi Mendoza Jan 10 '15 at 15:55
2
ClassName ClassName = new ClassName();

If you study compiler design course, you will know there is a Lexical Analysis step. At this step, you will write a grammar for your language. for example:

ClassName variableName = new ClassName();

So example above, compiler can understand second ClassName is variable.

When you do something like:

ClassName.doSomething();

Java will understand ClassName as variable rather than a class. And this design won't have any limitation. doSomething() can be both static method or just a instance method.

If Java understands ClassName here as class, so doSomething() cannot be a instance method. Maybe because this so Java creator has chosen above design: ClassName as a variable.

But what the problem if a variable name cannot be same name with their class. so the following example:

ClassA ClassB = new ClassA();
ClassB.callMethodInClassB();  // should compile error or not ???!!!

The problem still be here. The misleading still exist. So the new design should be:

No variable name should not has same name with **any** class name.

And you will see, this statement makes one language more complicate to understand and not so well-define. From above proofs, I think when you do something such as: A A = new A(); understand A as variable is a best way in language design.

Hope this help :)

hqt
  • 29,632
  • 51
  • 171
  • 250