【问题标题】:In Java, the variable name can be same with the classname在Java中,变量名可以与类名相同
【发布时间】:2015-01-10 15:35:42
【问题描述】:

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

所以我在下面的代码sn-p中有一个问题:编译器如何区分ClassName,是引用了变量名还是名?

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

class ClassName{}

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

【问题讨论】:

  • @LuiggiMendoza *lower camelCase.
  • "运行结果,编译器引用“类名”作为变量名。"这不是真的。是类名,不是变量名!
  • “我认为这是一个令人困惑和奇怪的设计”-@LuiggiMendoza 已经为您回答了这个问题。此外,您必须记住,如果有助于让事情变得更简单,您始终可以将 _ 与数字结合使用。

标签: java class compiler-construction


【解决方案1】:

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

ClassName ClassName = new ClassName();
    1         2               3

它可以看到 1 是类型名称应该在的位置,因此它知道您的意思是类。然后,2 是期望变量名的位置,因此它知道这应该是变量名。而 3new 关键字后面加上括号,所以它必须是一个类的名称。

System.out.println( ClassName );

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

为了自娱自乐,可以把打印语句改成:

System.out.println( ClassName.class );

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

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

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

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

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

【讨论】:

【解决方案2】:

编译器如何区分“类名”

因为有两个组成部分:变量类型和变量名。您声明了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. 允许它,在编译时解析引用时,首选静态方法。

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

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

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

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

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

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

【讨论】:

【解决方案3】:
ClassName ClassName = new ClassName();

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

ClassName variableName = new ClassName();

所以上面的例子,编译器可以理解 second 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理解为变量是语言设计中的最佳方式。

希望有帮助:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-20
    • 2020-11-06
    • 1970-01-01
    • 2019-05-17
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多