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