Java如何处理潜在的模棱两可的方法调用?

时间:2018-07-01 14:49:09

标签: java inheritance ambiguous

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

例如,具有以下继承结构:

public static class A {
}

public static class B extends A {

}
public static class C extends B {

}

并运行方法test()

public static void test() {
    test(new C(), new C(), new C());
}

由于某种原因,这两种方法不明确

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中使用多个参数确定准模棱两可的方法调用?

4 个答案:

答案 0 :(得分:3)

通读choosing the most specific method上的Java语言规范一章,需要注意以下声明:

  

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

     

... m2不是通用的,并且m1和m2适用于严格或   松散调用,其中m1具有形式参数类型S1,...,Sn   并且m2具有形式参数类型T1,...,Tn,类型Si更   对于所有i(1≤i≤n,n = k)的自变量ei,都比Ti特异。

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

因此,例如,当您拥有(A,A,B)和(A,A,C)时,后者更为具体,因为A = A并且C是B的子类,因此选择是明确且明确的

但是当您有(A,A,B)和(A,C,A)时,前者不能更具体,因为C是A的子类,但是后者也不能更具体,因为B是A的子类。因此,模棱两可。

答案 1 :(得分:0)

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)。第二个只是在提供更具体的用例(签名)时覆盖了前者。您可能希望将其视为覆盖前者,尽管这可能不是技术术语。

通过扩展名CA,因此,在选择参数为CA的调用方法的情况下,解释器将调用其中之一(因此可能会产生歧义)但必须使用更具体的参数(如果有)调用它。这可以描述为多态性。实例的运行时类型用于确定类,然后从降级的类开始搜索方法调用。

正如JB Nizet所指出的那样,语言规范是这里的权威,但我也想尝试一下。

答案 2 :(得分:0)

这是因为两种测试方法都可以接受C的实例作为其参数,因为C可以充当BA

答案 3 :(得分:0)

我尝试向您解释问题所在。

例如,我有一个包含三个方法的类,这些方法使用不同的参数来计算两个数字的乘法。

这些方法是:

  • long mul(int x,long y)
  • long mul(long x,int y)
  • long mul(long x,long 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)。

这是在使用重载时会发现的问题。

希望我能帮助您!