在Java中,变量名称可以与类名相同

时间:2015-01-10 15:35:42

标签: java class compiler-construction

在Java中,我可以声明一个变量,其名称与其类名完全相同。我认为这是一个令人困惑和奇怪的设计。

所以我在下面的代码片段中遇到了一个问题:编译器如何区分ClassName,它引用了变量名称或名称?

在运行结果中,编译器将ClassName引用为变量名。

class ClassName{}

public class Test {
    public static void main(String[] args){
        ClassName ClassName = new ClassName();
        System.out.println(ClassName); //ClassName@18fb53f6
    }
}

3 个答案:

答案 0 :(得分:17)

编译器可以通过上下文来判断。在您给出的示例中:

ClassName ClassName = new ClassName();
    1         2               3

可以看到 1 是类型名称应该是的位置,因此它知道你的意思是类。然后, 2 是期望变量名称的位置,因此它知道这应该是变量的名称。并且 3 在带有括号的new关键字之后,因此它必须是类的名称。

System.out.println( ClassName );

在这种情况下,ClassName处于参数传递的上下文中。类型名称不能作为参数传递,因此您必须表示变量的名称。

为了娱乐自己,您可以将print语句更改为:

System.out.println( ClassName.class );

将鼠标光标悬停在ClassName上,您将看到编译器将其识别为类的名称。然后将其更改为:

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

再次悬停光标,现在您看到它将其识别为变量名称。这是因为.class只能应用于类型名称,而getClass()只能应用于对象引用。在两种情况下,print语句的结果都是相同的 - 但是通过不同的机制。

所以编译器在这里没有问题。但你是对的,它对人类来说是不可读的。惯例是变量和方法的名称必须以小写字母开头,而类型名称必须以大写字母开头。遵守这一惯例将确保不会出现这种可读性问题。

我不能确切地说为什么Java的作者选择不强制执行这种约定(即,如果类型名称以小写字母开头或变量/方法名称以大写字母开头,则会产生编译器错误),但我推测他们不想做任何实际的错误,除非它实际上会导致编译器的歧义。编译错误应该表明编译器无法完成其工作的问题。

答案 1 :(得分:2)

  
    

编译器如何区分" Classname"

  

因为有两个组件:变量类型和变量名称。您声明类型为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规范中)。可能是以下之一:

  1. 不要让static和instance方法具有相同的名称。
  2. 允许它,在编译时解析引用时,更喜欢实例方法。
  3. 允许它,在编译时解析引用时,更喜欢静态方法。
  4. 如果最后2个中的任何一个,我想会记录编译器警告。

    现在编译器问题暂时搁置,代码的唯一其他消费者就是人类。编制者可能能够依靠规范来保证理性行为,但人类不能。我们很容易混淆。我对此的最好建议就是,不要这样做!

    绝对没有理由将变量命名为与类相同。实际上,我见过的大多数Java编码样式约定都使用lowerCamelCase命名变量和方法,使用UpperCamelCase命名类,因此除非你偏离标准,否则它们无法发生冲突。

    如果我在我正在处理的项目中遇到类似的代码,我会在做其他任何事情之前立即重命名变量。

    对于我的同名实例和静态方法的模糊情况,那里也可能有人类的教训:不要这样做!

    Java有很多规则迫使你做一些合乎逻辑的事情并使代码易于遵循,但最终,它仍然是代码,你可以编写你想要的任何代码。没有语言规范或编译器可以阻止您编写令人困惑的代码。

答案 2 :(得分:2)

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作为变量是语言设计的最佳方式。

希望这有帮助:)