Java参数混淆

Java参数混淆,java,parameters,dispatch,Java,Parameters,Dispatch,在下面的代码中,为什么o1.等于(o2)调用equals(Object o)而不是equals(EqualsTest et),即使o1和o2是引用类型为EqualsTest的对象 public class EqualsTest { public static <T> boolean equalTest(T o1, T o2) { return o1.equals(o2); } public static void main(St

在下面的代码中,为什么
o1.等于(o2)
调用
equals(Object o)
而不是
equals(EqualsTest et)
,即使
o1
o2
是引用类型为
EqualsTest
的对象

public class EqualsTest {
      public static <T> boolean equalTest(T o1, T o2) {
          return o1.equals(o2);
      }
      public static void main(String[] args) {
          EqualsTest et1 = new EqualsTest();
          EqualsTest et2 = new EqualsTest();
          System.out.println(et1.equals(et2));
          System.out.println(equalTest(et1, et2));
      }
      public boolean equals(Object o) {
          if (o instanceof EqualsTest) {
              System.out.println("equals(Object)");
              return true;
          }
          return false;
      }
      public boolean equals(EqualsTest et) {
          System.out.println("equals(EqualsTest)");
          return this.equals((Object)et);
      }
}
公共类EqualTest{
公共静态布尔等式测试(to1,to2){
返回o1。等于(o2);
}
公共静态void main(字符串[]args){
EqualTest et1=新EqualTest();
EqualTest et2=新EqualTest();
System.out.println(et1.equals(et2));
系统输出打印LN(相等测试(et1,et2));
}
公共布尔等于(对象o){
if(o EqualsTest实例){
System.out.println(“等于(对象)”;
返回true;
}
返回false;
}
公共布尔等于(相等测试集){
System.out.println(“等于(相等测试)”;
返回这个.equals((Object)et);
}
}

编译器根据而不是最具体的方法查找相应的方法。由于您没有为
t
指定任何内容,因此它默认为
对象
,正如@nicholas.hauschild正确指出的那样。

因为您使用了重载,所以方法在编译时被链接,Java使用不太具体的参数类型作为多态绑定到被调用方,在本例中是对象,而不是EqualTest。

正如我在注释中提到的,这是因为在Java中

检查为
equalTest
生成的字节码。您可以清楚地看到,它将调用以
对象
为参数的方法

这与调用this.equals((Object)et)相同,后者将调用
对象方法

// Method descriptor #15 (Ljava/lang/Object;Ljava/lang/Object;)Z
// Signature: <T:Ljava/lang/Object;>(TT;TT;)Z
// Stack: 2, Locals: 2
public static boolean equalTest(java.lang.Object o1, java.lang.Object o2);
0  aload_0 [o1]
1  aload_1 [o2]
2  invokevirtual java.lang.Object.equals(java.lang.Object) : boolean [18]

正如您所看到的,编译器已将
对象
更改为特定类型,即
相等测试
,因为我们使用了
有界类型
,所以现在如果调用
相等测试
,它将以
相等测试作为参数调用方法

类型擦除
正在执行其任务,您永远不应该编写
相等(X)
方法,但
等于(对象)
,仅供参考。它只会让你感到困惑,而且在你期望的时候不会被使用。@LouisWasserman是的,这就是我困惑的地方。我经常看到使用对象参数编写的方法,但是如果我知道我要传入一个EqualsTest对象,为什么我不能指定并将参数设置为EqualsTest?因为所有其他使用
equals
的代码,比如
HashSet
,都必须担心两个不同类型的对象可能不相等的情况,因此它必须使用
equals(Object)
版本,重载规则意味着它不会调用您的
equals(EqualsTest)
版本。一开始有点奇怪,但这确实是在所有情况下都有意义的唯一方法。@LouisWasserman这是否意味着如果我声明了
public static boolean equalTest(EqualsTest o1,EqualsTest o2)
,那么程序将调用
equals(EqualsTest et)
?人们可能会问的声明类型是什么?在这个场景中,它是,我们没有关于它的更多信息,所以我们唯一可以推断的是它是一个
对象+1@nicholas.hauschild但是我们不是引用了
et2=new EqualsTest()
,这样编译器就知道应该调用的是
equals(EqualsTest et)
。。。这就是我困惑的地方。不,一旦你使用静态泛型方法,这种知识就会“丢失”,因为参数只是键入为
Object
@ShiDoiSi。如果你说的是真的,那么用
equals(SomeClass obj)编写代码就没有意义了
处理类参数的每个方法都必须用
methodName(Object obj)
@user133466编写。如果参数类型在编译时完全匹配,它将调用此方法,例如,因为变量声明为该类型,他的困惑是Java没有使用运行时类型的参数进行调度。但是您的回答有助于澄清
T
缺少的类型实例化会变成
Object
@user133466您可以使用
javap
:-1,最不特定!=在编译时最精确,这就是实际发生的情况。(我不觉得那篇文章特别可读;)你所说的“最精确”是什么意思?澄清一下:编译器选择
对象
,因为这是它在compile type中唯一的类型。如果它有一个像@AmitD的答案中那样的“更好的”选项,它将使用该选项,而不是像您猜测的那样最不具体(仍然是
对象
)。或者你的意思是“…不太具体…”?我的意思是编译器知道的不太具体。这是正确的吗?试着详细说明你的答案…这是关于精确性的(字面上),所以在制定过程中也需要一些注意(正如你之前正确地指出的)。
public static <T extends EqualsTest> boolean equalTest(T o1, T o2) {
    return o1.equals(o2);
}
public static boolean equalTest(EqualsTest o1, EqualsTest o2);
0  aload_0 [o1]
1  aload_1 [o2]
2  invokevirtual EqualsTest.equals(EqualsTest) : boolean [18]
5  ireturn