什么决定了应该调用静态方法还是对象方法

3

我收到了一个普通的问题,是由于某人无意中给一个类的一个变量取了一个与另一个类相同名称的名字,而这两个类都有一个同名方法,其中一个是静态的。

考虑以下情况:

public class A {
    public static String dothis(String string){
        return "Class";
    }

    public static String dothis(String string, String string2){
        return "Class Method 2";
    }
}

并且这个

public class B {    
    public String dothis(String string){
        return "Object";
    }
}

一旦实例化对象,以下代码是否总是调用对象而不是静态方法?
System.out.println(A.dothis("..."));//outputs Class
B A = new B();
System.out.println(A.dothis("..."));//outputs Object

注意,实例化之后似乎不可能调用类A中的任何静态方法,即:

B A = new B();
System.out.println(A.dothis("..."));
System.out.println(A.dothis("...","..."));//Won't compile

以上代码无法编译,报告错误的树类型。
编辑:添加具体的异常信息:-
java.lang.RuntimeException: Uncompilable source code - Erroneous tree type: <any>
    at testbed.....

这是什么意思呢?编译器实际上在决定调用哪个方法,因此不同的javac版本可能会有不同的行为。

这里到底发生了什么,如果这是有保证的,是否可以以某种方式用于混淆,或者反编译会移除名称冲突吗?


4
这就是为什么有命名规范的原因 :) - Fildor
2
定义“抱怨错误的树类型”。错误消息应该准确地引用并完整地呈现,而不是改变其意义。编译器有权对任何违反JLS规定的事情提出抱怨,不能自动假设不同版本可以有不同的行为。 - user207421
哇,那真是个恶魔般的想法。这种可怕的黑客技巧我几乎喜欢上了它。 - Vlasec
3个回答

7
完成此操作后:
B A = new B();

...标识符A指的是变量,而不是类。

为了避免这成为问题,只需不要这样做...

这里到底发生了什么?如果这是确保的,它是否可以用于混淆,或者反编译会删除名称冲突?

完全取决于反编译器。如果它足够聪明,能够识别出这个问题,它可能会将本地变量重命名为不冲突的变量。


3
同意。这是尊重Java命名约定的另一个好理由:类以大写字母开头,变量以小写字母开头。因此,如果您遵守这些约定,就不可能出现这样的冲突。 - JB Nizet

2

Java规范中提到:

一个简单名称可能出现在可能被解释为变量、类型或包名的上下文中。在这些情况下,§6.5的规则指定优先选择变量而不是类型,优先选择类型而不是包名。因此,有时可能无法通过它的简单名称引用可见的类型或包声明。我们称这样的声明是被遮蔽的。

在您的示例中,A 引用了变量而不是类。如果 A 类别位于某个包中,则仍然可以使用以下方式访问静态方法:

com.apackage.A.doThis("");

0

在你的代码中,B A=new B() 是类B的本地对象变量,类A被隐藏了。

你有两个选项:

选项1:使用包名(命名空间名称)

    B A = new B();
    System.out.println(A.dothis("..."));
    System.out.println(Pack1.A.dothis("...","..."));//Class defined in Pack1 package .

选项1:重命名变量

    B Renamobj = new B();
    System.out.println(A.dothis("..."));
    System.out.println(A.dothis("...","..."));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接