简体   繁体   English

为什么对于char,编译器为什么更喜欢int重载而不是varargs char重载?

[英]Why does the compiler prefer an int overload to a varargs char overload for a char?

Code

public class TestOverload {

    public TestOverload(int i){System.out.println("Int");}
    public TestOverload(char... c){System.out.println("char");}

    public static void main(String[] args) {
        new TestOverload('a');
        new TestOverload(65);
    }
}

Output 产量

Int
Int

Is it expected behaviour? 这是预期的行为吗? If so, then why? 如果是这样,那为什么呢? I am expecting: char, Int 我期望:char,Int

Note: I am using Java 8 注意:我正在使用Java 8

Methods with varargs ( ... ) have the lowest priority when the compiler determines which overloaded method to choose. 当编译器确定选择哪种重载方法时,具有varargs( ... )的方法具有最低的优先级。 Therefore TestOverload(int i) is chosen over TestOverload(char... c) when you call TestOverload with a single char parameter 'a' , since a char can be automatically promoted to an int . 因此,当您使用单个char参数'a'调用TestOverload ,将选择TestOverload(int i)而不是TestOverload(char... c) ,因为可以将char自动提升为int

JLS 15.12.2 : JLS 15.12.2

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation . 第一阶段(第15.12.2.2节)执行重载解析, 而不允许装箱或拆箱转换,也不允许 使用可变arity方法调用 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. 这保证了,由于引入了可变arity方法,隐式装箱和/或拆箱,在Java SE 5.0之前的Java编程语言中有效的任何调用都不会被认为是模棱两可的。 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. 但是,声明可变可变方法(第8.4.1节)可以更改为给定方法方法调用表达式选择的方法,因为可变可变方法在第一阶段被视为固定可变方法。 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. 例如,在已经声明了m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),例如m(Object [] )更具体。

  2. 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 . 第二阶段(第15.12.2.3节)在允许装箱和拆箱的同时执行重载解析,但仍排除使用可变arity方法调用 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. 这样可以确保如果通过固定arity方法调用适用方法,则永远不会通过可变arity方法调用选择方法。

  3. The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods , boxing, and unboxing. 第三阶段(第15.12.2.4节) 允许将重载与可变arity方法 ,装箱和拆箱相结合

EDIT: 编辑:

It you wish to force the compiler to call the TestOverload(char... c) constructor, you can pass to the constructor call a char[] : 如果您希望强制编译器调用TestOverload(char... c)构造函数,则可以传递给构造函数调用char[]

new TestOverload (new char[] {'a'});

Yes, it is expected behaviour. 是的,这是预期的行为。 The precedence for method calling goes like this : 方法调用的优先级如下所示:

  1. Widending Widending
  2. Boxing 拳击
  3. Varargs 可变参数

Below is excerpt from Java docs related to same :- 以下摘录自与之相关的Java文档 :-

The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1). 确定适用性的过程始于确定可能适用的方法(第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. 该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言版本兼容。 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. 第一阶段(第15.12.2.2节)执行重载解析,不允许装箱或拆箱转换,也不允许使用可变arity方法调用。 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. 这保证了,由于引入了可变arity方法,隐式装箱和/或拆箱,在Java SE 5.0之前的Java编程语言中有效的任何调用都不会被认为是模棱两可的。 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. 但是,声明可变可变方法(第8.4.1节)可以更改为给定方法方法调用表达式选择的方法,因为可变可变方法在第一阶段被视为固定可变方法。 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. 例如,在已经声明了m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),例如m(Object [] )更具体。

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. 第二阶段(第15.12.2.3节)在允许装箱和拆箱的同时执行重载解析,但仍排除使用可变arity方法调用。 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. 这样可以确保如果通过固定arity方法调用适用方法,则永远不会通过可变arity方法调用选择方法。

The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing. 第三阶段(第15.12.2.4节)允许将重载与可变arity方法,装箱和拆箱相结合。

Solid advice from Joshua Bloch (Effective Java, 2nd Ed): Joshua Bloch(有效Java,第二版)的可靠建议:

"only choose as arguments for an overloaded method those that have -radically- different types." “仅选择具有根本不同类型的参数作为重载方法的参数。”

An object with a radically different type is one that can not reasonably be cast into another of the argument types. 具有完全不同类型的对象是无法合理地转换为另一个自变量类型的对象。 Following this rule can potentially save you hours of debugging a mysterious error that can happen when the compiler chooses at compile time the method overloading that you did not expect. 遵循此规则可以潜在地节省您调试神秘错误的时间,而当编译器在编译时选择您未曾期望的方法重载时,这种错误可能会发生。

Your lines of code violate this rule and open the door for bugs: 您的代码行违反了此规则,并为漏洞打开了大门:

public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}

A char is interconvertible with an int and so the only way you can predict what will happen with the invocations is to go to the Java Language Specification and read the somewhat arcane rules about how overloadings are resolved. char可以与int互转换,因此可以预测调用将发生的唯一方法是转到Java语言规范,并阅读一些有关如何解决重载的不可思议的规则。

Luckily, this situation shouldn't need JLS research. 幸运的是,这种情况不需要JLS研究。 If you have arguments that are not radically different from each other, probably the best option is to not overload . 如果您的参数彼此之间没有根本不同,那么最好的选择可能是不要重载 Give the methods different names so that there is no possibility for error or confusion on the part of anyone who may need to maintain the code. 为方法指定不同的名称,以便可能需要维护代码的任何人都不会出现错误或混乱。

Time is money. 时间就是金钱。

I took the code from this link and modified some parts of it: 我从此链接中获取了代码,并对其进行了一些修改:

    public static void main(String[] args) {
    Byte i = 5;
    byte k = 5;
    aMethod(i, k);
}

//method 1
static void aMethod(byte i, Byte k) {
    System.out.println("Inside 1");
}

//method 2
static void aMethod(byte i, int k) {
    System.out.println("Inside 2");
}

//method 3
static void aMethod(Byte i, Byte k) {
    System.out.println("Inside 3 ");
}

//method 4
static void aMethod(Byte  i, Byte ... k) {
    System.out.println("Inside 4 ");
}

The compiler gives error (The method is ambiguous for the type Overloading) for methods 1, 2 and 3 but not 4 (why?) 编译器针​​对方法1、2和3给出错误(对于重载类型,该方法不明确),但未给出4(为什么?)

The answer lies in the mechanism which java uses to match method calls to method signatures. 答案在于java用于将方法调用与方法签名进行匹配的机制。 The mechanism is done in three phases, in each phase if it finds matching method it stops: 该机制分三个阶段完成,在每个阶段,如果找到匹配的方法,它将停止:

+phase one: use widening to find matching method (no matching methods found) +第一阶段:使用扩展查找匹配方法(未找到匹配方法)

+phase two: (also) use boxing/unboxing to find matching method (method 1,2 and 3 match) +第二阶段:(也)使用装箱/拆箱查找匹配方法(方法1,2和3匹配)

+phase three: (also) use var args (method 4 matches!) +阶段三:(也)使用var args(方法4匹配!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM