The following is code compiles in Java 7, but not openjdk-1.8.0.45-31.b13.fc21.
以下是Java 7中的代码编译,但不是openjdk-1.8.0.45-31.b13.fc21。
static void f(Object o1, int i) {}
static void f(Object o1, Object o2) {}
static void test(boolean b) {
String s = "string";
double d = 1.0;
// The supremum of types 'String' and 'double' is 'Object'
Object o = b ? s : d;
Double boxedDouble = d;
int i = 1;
f(o, i); // fine
f(b ? s : boxedDouble, i); // fine
f(b ? s : d, i); // ERROR! Ambiguous
}
The compiler claims the last method call ambiguous.
编译器声称最后一个方法调用不明确。
If we change the type of the second parameter of f
from int
to Integer
, then the code compiles on both platforms. Why doesn't the posted code compile in Java 8?
如果我们将f的第二个参数的类型从int更改为Integer,那么代码将在两个平台上编译。为什么发布的代码不能在Java 8中编译?
2 个解决方案
#1
Let's first consider a simplified version that doesn't have a ternary conditional and doesn't compile on Java HotSpot VM (build 1.8.0_25-b17):
让我们首先考虑一个没有三元条件且不在Java HotSpot VM上编译的简化版本(build 1.8.0_25-b17):
public class Test {
void f(Object o1, int i) {}
void f(Object o1, Object o2) {}
void test() {
double d = 1.0;
int i = 1;
f(d, i); // ERROR! Ambiguous
}
}
The compiler error is:
编译器错误是:
Error:(12, 9) java: reference to f is ambiguous
both method f(java.lang.Object,int) in test.Test and method f(java.lang.Object,java.lang.Object) in test.Test match
According to JLS 15.12.2. Compile-Time Step 2: Determine Method Signature
根据JLS 15.12.2。编译时间步骤2:确定方法签名
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).
如果适用于严格调用(第15.12.2.2节),松散调用(第15.12.2.3节)或变量调用(第15.12.2.4节)之一,则该方法适用。
Invocation has to do with invocation context which is explained here JLS 5.3. Invocation Contexts
调用与调用上下文有关,这在JLS 5.3中有解释。调用上下文
When no boxing or unboxing is involved for a method invocation then strict invocation applies. When boxing or unboxing is involved for a method invocation then loose invocation applies.
如果方法调用不涉及装箱或拆箱,则应用严格调用。当方法调用涉及装箱或拆箱时,则应用松散调用。
Identifying applicable methods is divided into 3 phases.
确定适用的方法分为3个阶段。
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.
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。
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.
第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量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方法,装箱和拆箱相结合。
For our case there are no methods applicable by strict invocation. Both methods are applicable by loose invocation since the double value has to be boxed.
对于我们的情况,严格的调用没有适用的方法。这两种方法都适用于松散调用,因为必须将double值加到框中。
According to JLS 15.12.2.5 Choosing the Most Specific Method:
根据JLS 15.12.2.5选择最具体的方法:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。
Then:
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
对于具有参数表达式e1,...,ek的调用,如果满足以下任何条件,则一个适用的方法m1比另一个适用的方法m2更具体:
m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.
m2是通用的,并且根据§18.5.4推断m1对于参数表达式e1,...,ek比m2更具体。
m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
m2不是通用的,m1和m2适用于严格或松散的调用,并且m1具有形式参数类型S1,...,Sn和m2具有形式参数类型T1,...,Tn,类型Si更多对于所有i,参数ei的特定于Ti(1≤i≤n,n = k)。
m2 is not generic, and m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
m2不是通用的,m1和m2适用于变量arity调用,并且m1的前k个变量arity参数类型是S1,...,Sk和m2的前k个变量arity参数类型是T1,..对于所有i(1≤i≤k),对于参数ei,Si类型比Ti更具体。另外,如果m2具有k + 1个参数,则m1的第k + 1个可变参数类型是第m + 1个可变参数类型m2的子类型。
The above conditions are the only circumstances under which one method may be more specific than another.
上述条件是一种方法可能比另一种方法更具体的唯一情况。
A type S is more specific than a type T for any expression if S <: T (§4.10).
如果S <:T(§4.10),则类型S对于任何表达式比类型T更具体。
It may look that the 2nd condition matches for this case but in fact it doesn't because int is not a subtype of Object: it's not true that int <: Object. However if we replace int with Integer in the f method signature this condition would match. Note that the 1st parameter in methods matches this condition since Object <: Object is true.
可能看起来第二个条件匹配这种情况,但实际上并不是因为int不是Object的子类型:int <:Object不是真的。但是,如果我们在f方法签名中用Integer替换int,则此条件将匹配。请注意,方法中的第一个参数与此条件匹配,因为Object <:Object为true。
According to $4.10 no subtype/supertype relation is defined between primitive types and Class/Interface types. So int is not a subtype of Object for example. Thus int is not more specific than Object.
根据$ 4.10,在基本类型和类/接口类型之间没有定义子类型/超类型关系。因此int不是Object的子类型。因此int并不比Object更具体。
Since among the 2 methods there are no more specific methods thus there can be no strictly more specific and can be no most specific method (the JLS gives definitions for those terms in the same paragraph JLS 15.12.2.5 Choosing the Most Specific Method). So both methods are maximally specific.
由于在这两种方法中没有更具体的方法,因此没有严格的更具体的方法,也可能没有最具体的方法(JLS给出了同一段中JLS 15.12.2.5选择最具体方法的那些术语的定义)。所以这两种方法都是最具体的。
In this case the JLS gives 2 options:
在这种情况下,JLS提供了2个选项:
If all the maximally specific methods have override-equivalent signatures (§8.4.2) ...
如果所有最大特定方法都具有覆盖等效签名(第8.4.2节)......
This is not our case, thus
因此,这不是我们的情况
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
否则,方法调用不明确,并发生编译时错误。
The compile-time error for our case looks valid according to the JLS.
根据JLS,我们案例的编译时错误看起来有效。
What happens if we change method parameter type from int to Integer?
如果我们将方法参数类型从int更改为Integer会发生什么?
In this case both methods are still applicable by loose invocation. However the method with Integer parameter is more specific than the method with 2 Object parameters since Integer <: Object. The method with Integer parameter is strictly more specific and most specific thus the compiler will choose it and not throw a compile error.
在这种情况下,两种方法仍然适用于松散调用。但是,使用Integer参数的方法比具有2个Object参数的方法更具体,因为Integer <:Object。使用Integer参数的方法严格更具体,更具体,因此编译器将选择它而不抛出编译错误。
What happens if we change double to Double in this line: double d = 1.0;?
如果我们在这一行中将double改为Double会发生什么:double d = 1.0 ;?
In this case there is exactly 1 method applicable by strict invocation: no boxing or unboxing is required for invocation of this method: f(Object o1, int i). For the other method you need to do boxing of int value so it's applicable by loose invocation. The compiler can choose the method applicable by strict invocation thus no compiler error is thrown.
在这种情况下,正好有一种适用于严格调用的方法:调用此方法不需要装箱或拆箱:f(Object o1,int i)。对于另一种方法,您需要对int值进行装箱,因此它适用于松散调用。编译器可以选择严格调用适用的方法,因此不会抛出编译器错误。
As Marco13 pointed out in his comment there is a similar case discussed in this post Why is this method overloading ambiguous?
正如Marco13在他的评论中指出的那样,在这篇文章中讨论了类似的案例为什么这种方法过载模糊不清?
As explained in the answer there were some major changes related to the method invocation mechanisms between Java 7 and Java 8. This explains why the code compiles in Java 7 but not in Java 8.
正如答案中所解释的那样,Java 7和Java 8之间的方法调用机制有一些重大变化。这就解释了为什么代码在Java 7中编译而在Java 8中编译。
Now comes the fun part!
有趣的来了!
Let's add a ternary conditional operator:
让我们添加一个三元条件运算符:
public class Test {
void f(Object o1, int i) {
System.out.println("1");
}
void f(Object o1, Object o2) {
System.out.println("2");
}
void test(boolean b) {
String s = "string";
double d = 1.0;
int i = 1;
f(b ? s : d, i); // ERROR! Ambiguous
}
public static void main(String[] args) {
new Test().test(true);
}
}
The compiler complains about ambiguous method invocation. The JLS 15.12.2 doesn't dictate any special rules related to ternary conditional operators when performing method invocations.
编译器抱怨模糊的方法调用。在执行方法调用时,JLS 15.12.2没有规定与三元条件运算符相关的任何特殊规则。
However there are JLS 15.25 Conditional Operator ? : and JLS 15.25.3. Reference Conditional Expressions. The former one categorizes conditional expressions into 3 subcategories: boolean, numeric and reference conditional expression. The second and third operands of our conditional expression have types String and double respectively. According to the JLS our conditional expression is a reference conditional expression.
但是有JLS 15.25条件运算符? :和JLS 15.25.3。参考条件表达式。前者将条件表达式分为3个子类别:布尔值,数值和引用条件表达式。条件表达式的第二个和第三个操作数分别具有String和double类型。根据JLS,我们的条件表达式是引用条件表达式。
Then according to JLS 15.25.3. Reference Conditional Expressions our conditional expression is a poly reference conditional expression since it appears in an invocation context. The type of our poly conditional expression thus is Object (the target type in the invocation context). From here we could continue the steps as if the first parameter is Object in which case the compiler should choose the method with int as the second parameter (and not throw the compiler error).
然后根据JLS 15.25.3。参考条件表达式我们的条件表达式是一个多重引用条件表达式,因为它出现在调用上下文中。因此,多条件表达式的类型是Object(调用上下文中的目标类型)。从这里我们可以继续这些步骤,好像第一个参数是Object,在这种情况下,编译器应该选择使用int作为第二个参数的方法(而不是抛出编译器错误)。
The tricky part is this note from JLS:
棘手的部分是来自JLS的这个说明:
its second and third operand expressions similarly appear in a context of the same kind with target type T.
它的第二个和第三个操作数表达式同样出现在目标类型为T的相同类型的上下文中。
From this we can assume (also the "poly" in the name implies this) that in the context of method invocation the 2 operands should be considered independently. What this means is that when the compiler has to decide whether a boxing operation is required for such argument it should look into each of the operands and see if a boxing may be required. For our specific case String doesn't require boxing and double will require boxing. Thus the compiler decides that for both overloaded methods it should be a loose method invocation. Further steps are the same as in the case when instead of a ternary conditional expression we use a double value.
由此我们可以假设(也就是名称中的“poly”暗示这一点),在方法调用的上下文中,应该独立考虑2个操作数。这意味着当编译器必须决定是否需要对此类参数进行装箱操作时,它应该查看每个操作数并查看是否需要装箱。对于我们的具体情况String不需要拳击和双重将需要拳击。因此,编译器决定对于两个重载方法,它应该是一个松散的方法调用。进一步的步骤与我们使用双值而不是三元条件表达式的情况相同。
From the explanation above it seems that the JLS itself is vague and ambiguous in the part related to conditional expressions when applied to overloaded methods so we had to make some assumptions.
从上面的解释看,当应用于重载方法时,JLS本身在与条件表达式相关的部分中是模糊和模糊的,因此我们必须做出一些假设。
What's interesting is that my IDE (IntelliJ IDEA) doesn't detect the last case (with the ternary conditional expression) as a compiler error. All other cases it detects according to the java compiler from JDK. This means that either JDK java compiler or the internal IDE parser has a bug.
有趣的是我的IDE(IntelliJ IDEA)没有检测到最后一种情况(使用三元条件表达式)作为编译器错误。它根据JDK的java编译器检测到的所有其他情况。这意味着JDK java编译器或内部IDE解析器都有错误。
#2
In short:
The compiler doesn't know which method to choose since the ordering between primitive and reference types is not defined in JLS in regards to choosing most specific method.
编译器不知道选择哪种方法,因为在选择最具体的方法时,在JLS中没有定义原语和引用类型之间的排序。
When you use Integer instead of int the compiler picks the method with Integer because Integer is a subtype of Object.
当您使用Integer而不是int时,编译器会使用Integer选择方法,因为Integer是Object的子类型。
When you use Double instead of double the compiler picks the method that doesn't involve boxing or unboxing.
当您使用Double而不是double时,编译器会选择不涉及装箱或拆箱的方法。
Prior to Java 8 the rules were different so this code could compile.
在Java 8之前,规则是不同的,因此这段代码可以编译。
#1
Let's first consider a simplified version that doesn't have a ternary conditional and doesn't compile on Java HotSpot VM (build 1.8.0_25-b17):
让我们首先考虑一个没有三元条件且不在Java HotSpot VM上编译的简化版本(build 1.8.0_25-b17):
public class Test {
void f(Object o1, int i) {}
void f(Object o1, Object o2) {}
void test() {
double d = 1.0;
int i = 1;
f(d, i); // ERROR! Ambiguous
}
}
The compiler error is:
编译器错误是:
Error:(12, 9) java: reference to f is ambiguous
both method f(java.lang.Object,int) in test.Test and method f(java.lang.Object,java.lang.Object) in test.Test match
According to JLS 15.12.2. Compile-Time Step 2: Determine Method Signature
根据JLS 15.12.2。编译时间步骤2:确定方法签名
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).
如果适用于严格调用(第15.12.2.2节),松散调用(第15.12.2.3节)或变量调用(第15.12.2.4节)之一,则该方法适用。
Invocation has to do with invocation context which is explained here JLS 5.3. Invocation Contexts
调用与调用上下文有关,这在JLS 5.3中有解释。调用上下文
When no boxing or unboxing is involved for a method invocation then strict invocation applies. When boxing or unboxing is involved for a method invocation then loose invocation applies.
如果方法调用不涉及装箱或拆箱,则应用严格调用。当方法调用涉及装箱或拆箱时,则应用松散调用。
Identifying applicable methods is divided into 3 phases.
确定适用的方法分为3个阶段。
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.
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。
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.
第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量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方法,装箱和拆箱相结合。
For our case there are no methods applicable by strict invocation. Both methods are applicable by loose invocation since the double value has to be boxed.
对于我们的情况,严格的调用没有适用的方法。这两种方法都适用于松散调用,因为必须将double值加到框中。
According to JLS 15.12.2.5 Choosing the Most Specific Method:
根据JLS 15.12.2.5选择最具体的方法:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。
Then:
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
对于具有参数表达式e1,...,ek的调用,如果满足以下任何条件,则一个适用的方法m1比另一个适用的方法m2更具体:
m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.
m2是通用的,并且根据§18.5.4推断m1对于参数表达式e1,...,ek比m2更具体。
m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
m2不是通用的,m1和m2适用于严格或松散的调用,并且m1具有形式参数类型S1,...,Sn和m2具有形式参数类型T1,...,Tn,类型Si更多对于所有i,参数ei的特定于Ti(1≤i≤n,n = k)。
m2 is not generic, and m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
m2不是通用的,m1和m2适用于变量arity调用,并且m1的前k个变量arity参数类型是S1,...,Sk和m2的前k个变量arity参数类型是T1,..对于所有i(1≤i≤k),对于参数ei,Si类型比Ti更具体。另外,如果m2具有k + 1个参数,则m1的第k + 1个可变参数类型是第m + 1个可变参数类型m2的子类型。
The above conditions are the only circumstances under which one method may be more specific than another.
上述条件是一种方法可能比另一种方法更具体的唯一情况。
A type S is more specific than a type T for any expression if S <: T (§4.10).
如果S <:T(§4.10),则类型S对于任何表达式比类型T更具体。
It may look that the 2nd condition matches for this case but in fact it doesn't because int is not a subtype of Object: it's not true that int <: Object. However if we replace int with Integer in the f method signature this condition would match. Note that the 1st parameter in methods matches this condition since Object <: Object is true.
可能看起来第二个条件匹配这种情况,但实际上并不是因为int不是Object的子类型:int <:Object不是真的。但是,如果我们在f方法签名中用Integer替换int,则此条件将匹配。请注意,方法中的第一个参数与此条件匹配,因为Object <:Object为true。
According to $4.10 no subtype/supertype relation is defined between primitive types and Class/Interface types. So int is not a subtype of Object for example. Thus int is not more specific than Object.
根据$ 4.10,在基本类型和类/接口类型之间没有定义子类型/超类型关系。因此int不是Object的子类型。因此int并不比Object更具体。
Since among the 2 methods there are no more specific methods thus there can be no strictly more specific and can be no most specific method (the JLS gives definitions for those terms in the same paragraph JLS 15.12.2.5 Choosing the Most Specific Method). So both methods are maximally specific.
由于在这两种方法中没有更具体的方法,因此没有严格的更具体的方法,也可能没有最具体的方法(JLS给出了同一段中JLS 15.12.2.5选择最具体方法的那些术语的定义)。所以这两种方法都是最具体的。
In this case the JLS gives 2 options:
在这种情况下,JLS提供了2个选项:
If all the maximally specific methods have override-equivalent signatures (§8.4.2) ...
如果所有最大特定方法都具有覆盖等效签名(第8.4.2节)......
This is not our case, thus
因此,这不是我们的情况
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
否则,方法调用不明确,并发生编译时错误。
The compile-time error for our case looks valid according to the JLS.
根据JLS,我们案例的编译时错误看起来有效。
What happens if we change method parameter type from int to Integer?
如果我们将方法参数类型从int更改为Integer会发生什么?
In this case both methods are still applicable by loose invocation. However the method with Integer parameter is more specific than the method with 2 Object parameters since Integer <: Object. The method with Integer parameter is strictly more specific and most specific thus the compiler will choose it and not throw a compile error.
在这种情况下,两种方法仍然适用于松散调用。但是,使用Integer参数的方法比具有2个Object参数的方法更具体,因为Integer <:Object。使用Integer参数的方法严格更具体,更具体,因此编译器将选择它而不抛出编译错误。
What happens if we change double to Double in this line: double d = 1.0;?
如果我们在这一行中将double改为Double会发生什么:double d = 1.0 ;?
In this case there is exactly 1 method applicable by strict invocation: no boxing or unboxing is required for invocation of this method: f(Object o1, int i). For the other method you need to do boxing of int value so it's applicable by loose invocation. The compiler can choose the method applicable by strict invocation thus no compiler error is thrown.
在这种情况下,正好有一种适用于严格调用的方法:调用此方法不需要装箱或拆箱:f(Object o1,int i)。对于另一种方法,您需要对int值进行装箱,因此它适用于松散调用。编译器可以选择严格调用适用的方法,因此不会抛出编译器错误。
As Marco13 pointed out in his comment there is a similar case discussed in this post Why is this method overloading ambiguous?
正如Marco13在他的评论中指出的那样,在这篇文章中讨论了类似的案例为什么这种方法过载模糊不清?
As explained in the answer there were some major changes related to the method invocation mechanisms between Java 7 and Java 8. This explains why the code compiles in Java 7 but not in Java 8.
正如答案中所解释的那样,Java 7和Java 8之间的方法调用机制有一些重大变化。这就解释了为什么代码在Java 7中编译而在Java 8中编译。
Now comes the fun part!
有趣的来了!
Let's add a ternary conditional operator:
让我们添加一个三元条件运算符:
public class Test {
void f(Object o1, int i) {
System.out.println("1");
}
void f(Object o1, Object o2) {
System.out.println("2");
}
void test(boolean b) {
String s = "string";
double d = 1.0;
int i = 1;
f(b ? s : d, i); // ERROR! Ambiguous
}
public static void main(String[] args) {
new Test().test(true);
}
}
The compiler complains about ambiguous method invocation. The JLS 15.12.2 doesn't dictate any special rules related to ternary conditional operators when performing method invocations.
编译器抱怨模糊的方法调用。在执行方法调用时,JLS 15.12.2没有规定与三元条件运算符相关的任何特殊规则。
However there are JLS 15.25 Conditional Operator ? : and JLS 15.25.3. Reference Conditional Expressions. The former one categorizes conditional expressions into 3 subcategories: boolean, numeric and reference conditional expression. The second and third operands of our conditional expression have types String and double respectively. According to the JLS our conditional expression is a reference conditional expression.
但是有JLS 15.25条件运算符? :和JLS 15.25.3。参考条件表达式。前者将条件表达式分为3个子类别:布尔值,数值和引用条件表达式。条件表达式的第二个和第三个操作数分别具有String和double类型。根据JLS,我们的条件表达式是引用条件表达式。
Then according to JLS 15.25.3. Reference Conditional Expressions our conditional expression is a poly reference conditional expression since it appears in an invocation context. The type of our poly conditional expression thus is Object (the target type in the invocation context). From here we could continue the steps as if the first parameter is Object in which case the compiler should choose the method with int as the second parameter (and not throw the compiler error).
然后根据JLS 15.25.3。参考条件表达式我们的条件表达式是一个多重引用条件表达式,因为它出现在调用上下文中。因此,多条件表达式的类型是Object(调用上下文中的目标类型)。从这里我们可以继续这些步骤,好像第一个参数是Object,在这种情况下,编译器应该选择使用int作为第二个参数的方法(而不是抛出编译器错误)。
The tricky part is this note from JLS:
棘手的部分是来自JLS的这个说明:
its second and third operand expressions similarly appear in a context of the same kind with target type T.
它的第二个和第三个操作数表达式同样出现在目标类型为T的相同类型的上下文中。
From this we can assume (also the "poly" in the name implies this) that in the context of method invocation the 2 operands should be considered independently. What this means is that when the compiler has to decide whether a boxing operation is required for such argument it should look into each of the operands and see if a boxing may be required. For our specific case String doesn't require boxing and double will require boxing. Thus the compiler decides that for both overloaded methods it should be a loose method invocation. Further steps are the same as in the case when instead of a ternary conditional expression we use a double value.
由此我们可以假设(也就是名称中的“poly”暗示这一点),在方法调用的上下文中,应该独立考虑2个操作数。这意味着当编译器必须决定是否需要对此类参数进行装箱操作时,它应该查看每个操作数并查看是否需要装箱。对于我们的具体情况String不需要拳击和双重将需要拳击。因此,编译器决定对于两个重载方法,它应该是一个松散的方法调用。进一步的步骤与我们使用双值而不是三元条件表达式的情况相同。
From the explanation above it seems that the JLS itself is vague and ambiguous in the part related to conditional expressions when applied to overloaded methods so we had to make some assumptions.
从上面的解释看,当应用于重载方法时,JLS本身在与条件表达式相关的部分中是模糊和模糊的,因此我们必须做出一些假设。
What's interesting is that my IDE (IntelliJ IDEA) doesn't detect the last case (with the ternary conditional expression) as a compiler error. All other cases it detects according to the java compiler from JDK. This means that either JDK java compiler or the internal IDE parser has a bug.
有趣的是我的IDE(IntelliJ IDEA)没有检测到最后一种情况(使用三元条件表达式)作为编译器错误。它根据JDK的java编译器检测到的所有其他情况。这意味着JDK java编译器或内部IDE解析器都有错误。
#2
In short:
The compiler doesn't know which method to choose since the ordering between primitive and reference types is not defined in JLS in regards to choosing most specific method.
编译器不知道选择哪种方法,因为在选择最具体的方法时,在JLS中没有定义原语和引用类型之间的排序。
When you use Integer instead of int the compiler picks the method with Integer because Integer is a subtype of Object.
当您使用Integer而不是int时,编译器会使用Integer选择方法,因为Integer是Object的子类型。
When you use Double instead of double the compiler picks the method that doesn't involve boxing or unboxing.
当您使用Double而不是double时,编译器会选择不涉及装箱或拆箱的方法。
Prior to Java 8 the rules were different so this code could compile.
在Java 8之前,规则是不同的,因此这段代码可以编译。