Java 方法重载和可变参数
Java method overloading and varargs
我正在尝试了解方法重载,并且我有这些方法。
public void method(int a){
System.out.println("int a");
}
//implementing interface method
@Override
public void method() {
System.out.println("interface");
}
//varargs
public void method(int ... a){
System.out.println("int ... a");
}
使用这些参数调用它们后,
int[] a = new int[5];
stack.method();
stack.method(1);
stack.method(5,6);
stack.method(null);
stack.method(a);
我有这些结果:
interface
int a
int ... a
int ... a
int ... a
据我所知,由于歧义,该程序不应该编译,但它还是编译了。编译器不应该抛出错误吗?
方法重载决议分为三个阶段。第一阶段和第二阶段不将具有可变参数的方法(也称为可变参数方法)视为候选者,因此只有在没有找到不带可变参数的匹配方法时,编译器才会将具有可变参数的方法视为候选人。
因此,在第一次和第二次方法调用中,你的void method(int ... a)
被忽略了,没有歧义。
15.12.2. Compile-Time Step 2: Determine Method Signature
The second step searches the type determined in the previous step for
member methods. This step uses the name of the method and the argument
expressions to locate methods that are both accessible and applicable,
that is, declarations that can be correctly invoked on the given
arguments.
There may be more than one such method, in which case the most
specific one is chosen. The descriptor (signature plus return type) of
the most specific method is the one used at run time to perform the
method dispatch.
A method is applicable if it is applicable by one of strict invocation
(§15.12.2.2), loose invocation (§15.12.2.3), or variable arity
invocation (§15.12.2.4).
Certain argument expressions that contain implicitly typed lambda
expressions (§15.27.1) or inexact method references (§15.13.1) are
ignored by the applicability tests, because their meaning cannot be
determined until a target type is selected.
Although the method invocation may be a poly expression, only its
argument expressions - not the invocation's target type - influence
the selection of applicable methods.
The process of determining applicability begins by determining the
potentially applicable methods (§15.12.2.1).
The remainder of the process is split into three phases, to ensure
compatibility with versions of the Java programming language prior to
Java SE 5.0. The phases are:
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity
method invocation. If no applicable method is found during this phase
then processing continues to the second phase.
This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous
as the result of the introduction of variable arity methods, implicit
boxing and/or unboxing. However, the declaration of a variable arity
method (§8.4.1) can change the method chosen for a given method method
invocation expression, because a variable arity method is treated as a
fixed arity method in the first phase. For example, declaring
m(Object...) in a class which already declares m(Object) causes
m(Object) to no longer be chosen for some invocation expressions (such
as m(null)), as m(Object[]) is more specific.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable
arity method invocation. If no applicable method is found during this
phase then processing continues to the third phase.
This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method
invocation.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
只有在所有其他可能性都用尽后,编译器才会考虑具有可变参数列表的方法。
这些"other possibilities"按正常方式考虑。
因此在您的情况下没有歧义,因此编译器不会发出错误。
不,没有歧义:passing "(5,6)"
很好,因为该方法需要很多整数,传递 "(a)"
也很好,因为 a 是一个整数数组 passing"(null)"
也是很好,因为 null
可以转换为任何引用类型,例如 integer []
,因此它可以用在您期望 int [];
的地方
所以所有这些调用都调用了第三种方法
public void method(int ... a){
System.out.println("int ... a");
}
前两个方法调用是不言自明的,它们调用方法
public void method(){
System.out.println("interface");
}
和
public void method(int a){
System.out.println("int a");
}
分别
Eran 和 Bathsheba 已经说过为什么选择了不使用 null
的各种对象。
剩下的问题是:为什么 stack.method(null);
甚至可以编译?
答案是它匹配可变参数签名,因为可变参数 method(int...)
从编译器的角度来看实际上与 method(int[])
相同。由于数组是通过引用引用的,因此可以在需要 int[]
的地方使用 null
。
所以:
stack.method();
与界面中的 method()
签名完全匹配。与 method(int...)
没有歧义,因为仅当其他参数不匹配时才考虑可变参数。
stack.method(1);
匹配 method(int)
。不歧义,同上。
stack.method(5,6);
匹配 method(int...)
因为 none 非可变参数匹配,但可变参数匹配。
stack.method(null);
参见前面的解释。
stack.method(a);
匹配 match(int...)
的原因与 method(null0
相同:因为 match(int...)
对于编译器来说实际上与 match(int[])
相同。
我正在尝试了解方法重载,并且我有这些方法。
public void method(int a){
System.out.println("int a");
}
//implementing interface method
@Override
public void method() {
System.out.println("interface");
}
//varargs
public void method(int ... a){
System.out.println("int ... a");
}
使用这些参数调用它们后,
int[] a = new int[5];
stack.method();
stack.method(1);
stack.method(5,6);
stack.method(null);
stack.method(a);
我有这些结果:
interface
int a
int ... a
int ... a
int ... a
据我所知,由于歧义,该程序不应该编译,但它还是编译了。编译器不应该抛出错误吗?
方法重载决议分为三个阶段。第一阶段和第二阶段不将具有可变参数的方法(也称为可变参数方法)视为候选者,因此只有在没有找到不带可变参数的匹配方法时,编译器才会将具有可变参数的方法视为候选人。
因此,在第一次和第二次方法调用中,你的void method(int ... a)
被忽略了,没有歧义。
15.12.2. Compile-Time Step 2: Determine Method Signature
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch.
A method is applicable if it is applicable by one of strict invocation (§15.12.2.2), loose invocation (§15.12.2.3), or variable arity invocation (§15.12.2.4).
Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until a target type is selected.
Although the method invocation may be a poly expression, only its argument expressions - not the invocation's target type - influence the selection of applicable methods.
The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1).
The remainder of the process is split into three phases, to ensure compatibility with versions of the Java programming language prior to Java SE 5.0. The phases are:
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase. This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing. However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase. For example, declaring m(Object...) in a class which already declares m(Object) causes m(Object) to no longer be chosen for some invocation expressions (such as m(null)), as m(Object[]) is more specific.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase. This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
只有在所有其他可能性都用尽后,编译器才会考虑具有可变参数列表的方法。
这些"other possibilities"按正常方式考虑。
因此在您的情况下没有歧义,因此编译器不会发出错误。
不,没有歧义:passing "(5,6)"
很好,因为该方法需要很多整数,传递 "(a)"
也很好,因为 a 是一个整数数组 passing"(null)"
也是很好,因为 null
可以转换为任何引用类型,例如 integer []
,因此它可以用在您期望 int [];
所以所有这些调用都调用了第三种方法
public void method(int ... a){
System.out.println("int ... a");
}
前两个方法调用是不言自明的,它们调用方法
public void method(){
System.out.println("interface");
}
和
public void method(int a){
System.out.println("int a");
}
分别
Eran 和 Bathsheba 已经说过为什么选择了不使用 null
的各种对象。
剩下的问题是:为什么 stack.method(null);
甚至可以编译?
答案是它匹配可变参数签名,因为可变参数 method(int...)
从编译器的角度来看实际上与 method(int[])
相同。由于数组是通过引用引用的,因此可以在需要 int[]
的地方使用 null
。
所以:
stack.method();
与界面中的 method()
签名完全匹配。与 method(int...)
没有歧义,因为仅当其他参数不匹配时才考虑可变参数。
stack.method(1);
匹配 method(int)
。不歧义,同上。
stack.method(5,6);
匹配 method(int...)
因为 none 非可变参数匹配,但可变参数匹配。
stack.method(null);
参见前面的解释。
stack.method(a);
匹配 match(int...)
的原因与 method(null0
相同:因为 match(int...)
对于编译器来说实际上与 match(int[])
相同。