具有相同名称但类型不同的变量
具有相同名称但类型不同的变量
我在这里读到,在Java中,同一作用域中可以存在两个具有相同名称但不同类型的变量。我的意思是这样的:\n
class test { private int x; private double x; }
\n但是所有的Java IDE都不允许这样的代码。我想知道这样的代码是否真的在语法上是正确的,还是简单地IDE不允许这样的代码以防止歧义。\n无论如何,这是从网站上摘录的内容:\n
\n\"如果你幸运的话,你可能能够重新编译Jad的输出。然而,Java虚拟机对变量命名的规则比Java语言本身更宽松。例如,一个有效的类文件可以有几个名为\'a\'的变量,只要它们具有不同的类型。如果你反编译这样的类,你得到的源代码将不是有效的。\nJAD通常会重命名这些有问题的字段,并生成一个可重新编译的文件...唯一的问题是重新编译后的文件与原始类不兼容。\"\n
问题的出现原因是根据语言规范,在一个类声明的主体中,声明两个具有相同名称的字段是编译时错误。然而,这个规则是针对类文件而不是源代码的。
解决方法是,如果类型不同,NetBeans可以接受具有相同名称的两个变量,但是Eclipse不接受。通过实验证实,当在NetBeans 7.2.1中尝试声明具有相同名称但类型不同的变量时,第二个变量会被标记为错误(已在类中定义了变量)。
关于如何在源代码无法编译的情况下生成类文件,一种可能的解释是,编译后的类文件可能包含多个具有相同名称的变量。因此,当反编译工具需要将类文件转换为Java源代码文件时,会将这些重复变量重命名,以确保生成的代码可以编译。
另外,编译后的类文件不存储方法作用域的变量名,因此反编译器会将每个方法体的局部字段都应用一些无意义的“a”名称。
在同一作用域中,不允许存在变量名称相同但类型不同的变量。如果允许存在这种情况,Java编译器将无法确定你想引用哪个变量。
考虑以下代码片段:
class test { private int x; private double x; test() { //构造函数 System.out.println(x); //错误,无法确定引用的是哪个x } }
Java编译器无法理解你实际上引用的是哪个x。因此,这样的代码在语法上是错误的,无法编译通过。
然而,存在一些工具(例如ClassEditor)可以在生成的类文件创建之后修改它。在这里,可以将两个变量的名称更改为相同。然而,这样的类不一定可以在Java虚拟机中运行。
你提到的软件JAD可以重命名类文件中重复命名的变量,以便你获得的源代码实际上是符合语法规范的。
在Java中,变量不能具有相同的名称但不同的类型,这是非法的。然而,在字节码中是合法的。
问题的出现原因是由于使用了Java中的assert
关键字,该关键字在Oracle JDK 1.8.0_45中会生成多个具有相同名称但不同类型的字段。例如,在下面的示例中,Assert
类中使用了assert
关键字:
public class Assert { // We can't use a primitive like int here or it would get inlined. static final int[] $assertionsDisabled = new int[0]; public static void main(String[] args) { System.out.println($assertionsDisabled.length); // currentTimeMillis so it won't get optimized away. assert System.currentTimeMillis() == 0L; } }
assert
关键字会生成一个合成字段bool $assertionsDisable
来缓存一个方法调用。通过观察生成的字节码,可以看到常量表中复用了变量名#12
。
如果我们声明了另一个布尔变量,将无法编译通过,报错信息如下:
the symbol $assertionsDisabled conflicts with a compile synthesized symbol
这也是为什么在变量名中使用美元符号($)是一个非常糟糕的主意。
我们还可以使用Jasmin来验证这个问题。在下面的示例中,我们使用Jasmin语言编写了一个包含两个静态字段的类,一个是整型(I
),一个是浮点型(F
):
.class public FieldOverload .super java/lang/Object .field static f I .field static f F .method public static main([Ljava/lang/String;)V .limit stack 2 ldc 1 putstatic FieldOverload/f I ldc 1.5 putstatic FieldOverload/f F getstatic java/lang/System/out Ljava/io/PrintStream; getstatic FieldOverload/f I invokevirtual java/io/PrintStream/println(I)V getstatic java/lang/System/out Ljava/io/PrintStream; getstatic FieldOverload/f F invokevirtual java/io/PrintStream/println(F)V return .end method
这个示例中的两个静态字段分别是一个整型和一个浮点型。运行这段代码会输出:
1 1.5
这是因为Jasmin使用了两个不同类型的Fieldref
来区分它们。
变量具有相同名称但不同类型的问题在Java中是非法的,但在字节码中是合法的。要解决这个问题,可以避免使用具有相同名称的不同类型的变量,或者使用Jasmin等编译器来生成字节码时,确保使用不同类型的Fieldref
来区分它们。