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

In Java, the variable name can be same with the classname

在 Java 中我可以声明一个变量,其名称与它的 class 名称完全相同。我认为这是一个非常混乱和奇怪的设计。

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

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

class ClassName{}

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

how can the compiler distinguish the "Classname"

因为有两个组成部分:变量类型和变量名。您声明了一个 ClassName 类型的变量 ClassName。类型总是第一位的。 类 不是 first-class 对象(这意味着你不能引用 class),除非你进入反射(使用 .class 属性 ).

因此,在打印语句中:

System.out.println(ClassName);

那只能是变数。 System.out.println 采用对象引用,并且您有一个由名为 ClassName 的变量引用的对象,因此编译器可以解析它。

我认为编译器唯一不明确的情况是,如果变量引用一个对象,该对象具有与 class.[=18= 上的静态方法同名的实例方法]

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. 允许,并且在编译时解析引用时,首选静态方法。

如果是最后两个中的任何一个,我想会记录一个编译器警告。

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

绝对没有理由将变量命名为与 class 相同的变量。事实上,我见过的大多数 Java 编码风格约定使用 lowerCamelCase 命名变量和方法,使用 UpperCamelCase 命名 classes,所以除非你偏离标准,否则它们不可能发生冲突。

如果我在我正在处理的项目中遇到这样的代码,我会立即重命名该变量,然后再做其他事情。

对于我的同名实例和静态方法的模棱两可的情况,那里可能也有人给我们上了一课:不要这样做!

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

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

ClassName ClassName = new ClassName();
    1         2               3

它可以看到 1 是类型名称应该出现的位置,因此它知道您指的是 class。然后,2 是期望变量名的地方,因此它知道这应该是变量名。而 3 是在带括号的 new 关键字之后,所以它必须是 class.

的名称
System.out.println( ClassName );

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

自娱自乐,可以将打印语句改成:

System.out.println( ClassName.class );

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

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

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

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

我不能确切地说出为什么 Java 的作者选择不强制执行此约定(也就是说,如果类型名称以小写字母开头或 variable/method 名称开头,则给出编译器错误大写),但我推测他们不想让任何事情成为实际错误,除非它实际上会导致 编译器 产生歧义。编译错误应该表明存在使编译器无法工作的问题。

ClassName ClassName = new ClassName();

如果学过编译器设计课程,就会知道有一个词法分析步骤。在此步骤中,您将为您的语言编写语法。例如:

ClassName variableName = new ClassName();

所以上面的例子,编译器可以理解second ClassName是可变的

当您执行以下操作时:

ClassName.doSomething();

Java 会将 ClassName 理解为变量而不是 class。而且这种设计不会有任何限制。 doSomething() 可以是静态方法也可以只是实例方法。

如果Java把这里的ClassName理解为class,那么doSomething()就不能是实例方法。可能是因为这样 Java 创建者选择了上面的设计:ClassName 作为变量。

但是如果变量名 不能 与它们的 class 同名,那会有什么问题呢?所以下面的例子:

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

希望对您有所帮助:)