Java如何处理可能不明确的方法调用?

Java如何处理可能不明确的方法调用?,java,inheritance,ambiguous,Java,Inheritance,Ambiguous,在使用具有多个参数的模糊方法调用时,我注意到,在我预期的情况下,它通常实际上并不模糊,这导致了一些我无法完全理解的奇怪行为 例如,使用以下继承结构: public static class A { } public static class B extends A { } public static class C extends B { } 并运行方法test() 出于某种原因,这两种方法是不明确的 public static void test(A x, A xx, B xxx) {

在使用具有多个参数的模糊方法调用时,我注意到,在我预期的情况下,它通常实际上并不模糊,这导致了一些我无法完全理解的奇怪行为

例如,使用以下继承结构:

public static class A {
}

public static class B extends A {

}
public static class C extends B {

}
并运行方法
test()

出于某种原因,这两种方法是不明确的

public static void test(A x, A xx, B xxx) {
    System.out.println("TEST 1");
}

public static void test(A x, C xx, A xxx) {
    System.out.println("TEST 2");
}
但是,在第二个方法中交换最后两个参数会使该参数优先

public static void test(A x, A xx, B xxx) {
    System.out.println("TEST 1");
}

public static void test(A x, A xx, C xxx) {
    System.out.println("TEST 2"); //No longer ambiguous, this one is called
}
有人能解释一下这种行为吗,以及在Java中如何使用多个参数准确地确定准不明确的方法调用吗

public static void test(A x, A xx, B xxx) {
    System.out.println("TEST 1");
}

public static void test(A x, C xx, A xxx) {
    System.out.println("TEST 2");
}
这里的问题是这两种方法都适用于
(A、C、B)
。在这种特殊情况下,存在歧义

对于您提供的明确示例,两个声明没有共同的签名:

public static void test(A x, A xx, B xxx) {
    System.out.println("TEST 1");
}

public static void test(A x, A xx, C xxx) {
    System.out.println("TEST 2"); //No longer ambiguous, this one is called
}
第一种适用于
(A,A,B)
,第二种适用于
(A,A,C)
。第二个只是在提供更具体的用例(签名)时覆盖前者。你可能想把它看作是超越前者,尽管这可能不是技术术语。 扩展而言,
C
是一个
a
,因此在参数为
C
a
的情况下,如果选择调用方法,解释器将调用其中一个(因此可能出现歧义),但必须调用具有更具体参数的方法(如果可用)。这可以描述为多态性;实例的运行时类型用于确定类,方法调用从下一个类开始搜索


正如JB Nizet所指出的,语言规范是这里的权威,但我也喜欢实验。

这是因为两种测试方法都可以接受
C
的实例作为其参数,因为
C
可以充当
B
A
阅读Java语言规范一章,以下陈述值得注意:

一种适用方法m1比另一种适用方法更具体 方法m2,对于参数表达式e1,…,ek的调用, 如果

…m2不是通用的,m1和m2通过严格的或 松散调用,其中m1具有形式参数类型S1,…,Sn m2有形式参数类型T1,…,Tn,类型Si更多 所有i(1)的参数ei比Ti具体≤ 我≤ n、 n=k)

这基本上意味着,给定两个(非varargs)可以匹配一个方法调用的方法签名,编译器将选择更具体的方法,而更具体的方法是其中每个参数比另一个签名中的相应参数更具体(即与另一个签名中的相应参数相同的类或其子类)(为了简单起见,这里忽略泛型)

例如,当你有(A,A,B)和(A,A,C)时,后者更具体,因为A=A,C是B的一个子类,所以选择是明确的


但是当你有(A,A,B)和(A,C,A)时,前者不能更具体,因为C是A的一个子类,而后者也不能更具体,因为B是A的一个子类。因此,模糊性。

我试着向你解释问题所在

例如,我有一个类,它包含三个具有不同参数的方法来计算两个数的乘法

这些方法是:

  • 长mul(整数x,长y)
  • 长mul(长x,内y)
  • 长mul(长x,长y)
因为重载所有这些方法都是有效的,并且计算相同的东西:x和y之间的乘法结果

假设有两个变量作为方法的参数:

int x;
long y;
  • 如果我调用mul(x,y),那么我将得到x*y,因为mul(int,long)是最长的 这些参数的具体方法
  • 如果我调用mul((long)x,y),那么我将再次得到x*y,因为mul(long,long)是这些参数最具体的方法
  • 相反,如果我调用mul(x,x),我不会得到x*x,因为语言(Java)无法猜测参数的最佳转换是什么
调用mul(x,x)会导致这样的困境:我应该将x转换为long,所以调用mul(long,long)或者我应该将y转换为int,所以调用mul(int,int)

这是在使用重载时可以发现的问题


我希望我能帮助你!

我只想指出,你所描述的不是“多重继承”,而是一系列单一继承。@bhspencer我的错,我删除了标记,我几乎可以肯定这是重复的。只需查看方法重写的规则。这由Java语言规范的规则决定().它们又长又复杂。没有人知道它们的全部。但这不是问题,因为没有人使用这种含糊不清的重载,因为它们很难正确。
int x;
long y;