在 Java 中,什么时候向方法传递参数会变得不明确?

Dil*_*ris 5 java overloading parameter-passing

当方法重载完成后,我知道我们只能创建具有相同名称的方法,只要它们的方法签名不同。

class Demo{
    public static void myMethod(int y, double x){}
    public static void myMethod(double x,int y){}

    public static void main(String args[]){
        byte b=10;
        myMethod(b,b);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面显示的代码给出错误,提示错误:对 myMethod 的引用不明确 出现此问题的原因是字节值在自动转换后可以分配给 int 和 double 类型,并且对于要传递的值对哪个方法感到困惑?如果我错了请纠正我..

我尝试了以下程序。我以为这也会出错,但编译时没有错误

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        MyClass.myMethod(100);
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为它也会给出与之前相同的错误,但这给出的输出为myMethod(int) ...所以我假设由于它有一个可以传递 int 值的完美匹配方法,所以它不会给出错误。 。

但是如果我对上面的第二个程序进行以下更改,为什么它不会给出错误?

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        byte b=10;
        MyClass.myMethod(b);
    }
}
Run Code Online (Sandbox Code Playgroud)

byte 可以自动转换成 int 和 double 对吗?输出被给出为myMethod(int) .. 这不应该让编译器感到困惑,并且对myMethod 的错误引用不明确吗

dav*_*xxx 4

我不会指定和详细说明编译器用来决定必须调用哪个方法的所有规则,因为它包含其他条件。\n我将只关注您问题的方法参数标准。\n

\n\n

15.12.2.5 中。选择最具体的方法,您将获得宝贵的信息:

\n\n

非正式的直觉是,如果第一个方法处理的任何调用可以传递给另一个方法而不会出现编译时类型错误,则一个方法比另一个方法更具体。

\n\n

在您的示例中,这里是 JLS 应该回答您的问题的部分:

\n\n
\n

如果满足以下所有条件,则一个名为 m 的固定数量成员方法比另一个同名和数量的成员方法更具体:

\n\n
    \n
  • 第一个成员方法的参数声明类型为T1,...,Tn。

  • \n
  • 另一个方法的参数声明类型为U1,...,Un。

  • \n
  • 如果第二个方法是泛型的,则令 R1 ... Rp (p \xe2\x89\xa5 1) 为其类型参数,令 Bl 为 Rl (1 \xe2\x89\xa4 l \xe2\x89 \xa4 p),让\n A1 ... Ap 是在初始约束 Ti << Ui (1 \xe2\x89\xa4 i 下,此\n 调用推断的类型参数 (\xc2\xa715.12.2.7) \xe2\x89\xa4 n),令\n Si = Ui[R1=A1,...,Rp=Ap] (1 \xe2\x89\xa4 i \xe2\x89\xa4 n)。

    \n\n

    否则,令 Si = Ui (1 \xe2\x89\xa4 i \xe2\x89\xa4 n)。

  • \n
  • 对于从 1 到 n 的所有 j,Tj <: Sj。

  • \n
  • 如果第二方法是如上所述的通用方法,则Al <: Bl[R1=A1,...,Rp=Ap] (1 \xe2\x89\xa4 l \xe2\x89\xa4 p)。

  • \n
\n
\n\n

您应该感兴趣的是For all j from 1 to n, Tj <: Sj

\n\n

在编译时,如果已经确定了几种适用的方法,则选择最具体的一种。
\n然而,如果多个方法具有有效参数类型的最大特异性,则编译器不知道应该调用哪个方法。所以它会发出编译错误。

\n\n

您可以在 JLS 中找到此信息:15.12.2。编译时步骤 2:确定方法签名

\n\n
\n

如果一个方法是可访问和适用的,并且不存在其他严格更具体的可应用和可访问的方法,则该方法被认为对于方法调用是最大特定的。

\n\n

如果只有一个最具体的方法,那么该方法实际上是最具体的方法;它必然比任何其他适用的可访问方法更具体。然后对其进行一些进一步的编译时检查,如 \xc2\xa715.12.3 中所述。

\n\n

可能没有一个方法是最具体的,因为有两个或多个方法是最具体的。在这种情况下:

\n\n

- If all the maximally specific methods have override-equivalent (\xc2\xa78.4.2) signatures, then:

\n\n
    \n
  • 如果最具体的方法之一未声明为抽象,则它是最具体的方法。

  • \n
  • 否则,如果所有最大具体方法都被声明为抽象,并且所有最大具体方法的签名具有相同的擦除 (\xc2\xa74.6),则在其中任意选择最具体的方法。具有最具体返回类型的最具体方法的子集。

  • \n
  • 但是,当且仅当在每个最具体方法的 throws 子句中声明该异常或其擦除时,最具体的方法才被视为抛出检查异常。

  • \n
\n\n

- Otherwise, we say that the method invocation is ambiguous, and a\n compile-time > error occurs.

\n
\n\n

如果我们将这些规则应用于您的三个示例(我更改了顺序,以编译正常的情况开始,以编译错误的情况结束)。

\n\n

1)找到一种最具体的方法

\n\n
class MyClass{\n    public static void myMethod(int i){\n        System.out.println("myMethod1(int)");\n    }\n    public static void myMethod(double a){\n        System.out.println("myMethod2(int)");\n    }   \n}\n\nclass Demo{\n    public static void main(String args[]){\n        MyClass.myMethod(100);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译器看到一个具有完美匹配的方法:myMethod(int i)因为传递的值是一个int. 它编译得很好。

\n\n

2)找到一种最具体的方法

\n\n
class MyClass{\n    public static void myMethod(int i){\n        System.out.println("myMethod1(int)");\n    }\n    public static void myMethod(double a){\n        System.out.println("myMethod2(int)");\n    }   \n}\n\nclass Demo{\n    public static void main(String args[]){\n        byte b=10;\n        MyClass.myMethod(b);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译器认为一种方法比另一种方法具有更高的特异性。
\n根据扩大原始转换规则,从byte到 的隐式int转换确实比从byte到的隐式转换更具体。double

\n\n

我们可以检查这 比第一个方法处理的任何调用是否可以传递给另一个方法而不出现编译时类型错误void myMethod(int i)更具体 。void myMethod(double a)

\n\n

myMethod(3);适用于void myMethod(double x)编译良好。
\n但myMethod(double)3);应用于会void myMethod(int y)产生编译错误。
\n因此找到了一种独特的最大具体方法:void myMethod(int i)
\n编译没问题。

\n\n

3)未找到唯一的最大特定方法

\n\n
class Demo{\n    public static void myMethod(int y, double x){}\n    public static void myMethod(double x,int y){}\n\n    public static void main(String args[]){\n        byte b=10;\n        myMethod(b,b);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译器会看到两种方法,其中没有一种方法的特异性高于另一种方法。
\n首先,在这两种情况下,都需要将有效参数类型隐式转换为方法参数的声明类型。
\n但在这两种情况下,特异性是相同的。

\n\n

我们可以检查这一点 void myMethod(int y, double x)是否具体,就好像 myMethod(double x,int y)由 any 方法处理的任何调用都不能在没有编译时类型错误的情况下传递给另一个方法一样。

\n\n

myMethod(double)3,4);应用于会void myMethod(int y, double x)产生编译错误,因为int变量无法接受double值。
\n并且出于同样的原因myMethod(3,(double)4);应用于会void myMethod(double x,int y)产生编译错误。

\n\n

没有找到独特的最大特异性方法。所以编译器无法猜测应该调用哪个方法。出现编译错误。

\n