示例代码为:
public class OverloadingTest {
public static void test(Object obj){
System.out.println("Object called");
}
public static void test(String obj){
System.out.println("String called");
}
public static void main(String[] args){
test(null);
System.out.println("10%2==0 is "+(10%2==0));
test((10%2==0)?null:new Object());
test((10%2==0)?null:null);
}
输出是:
字符串称为
10%2 == 0为真 对象叫做
字符串称为
对test(null)
的第一次调用使用String
参数调用该方法,根据The Java Language Specification
可以理解。
1)任何人都可以解释我在前面的调用中调用test()
的基础吗?
2)当我们提出时,再说if
条件:
if(10%2==0){
test(null);
}
else
{
test(new Object());
}
它始终使用String
参数调用方法。
编译时编译器会计算表达式(10%2)
吗?我想知道表达式是在编译时还是在运行时计算的。感谢。
答案 0 :(得分:20)
Java使用早期绑定。在编译时选择最具体的方法。通过参数数量和参数类型选择最具体的方法。在这种情况下,参数数量无关紧要。这给我们留下了参数类型。
参数有哪些类型?两个参数都是表达式,使用三元条件运算符。问题简化为:条件三元运算符返回什么类型?类型在编译时计算。
给出两个表达式:
(10%2==0)? null : new Object(); // A
(10%2==0)? null : null; // B
列出了here类型评估规则。在B
中很容易,两个术语完全相同:null
将被返回(whatever type that may be)(JLS:"如果第二个和第三个操作数具有相同的类型(这可能是null类型),那么这就是条件表达式的类型。")。在A
中,第二个词来自特定的类。由于这是更具体的,null
可以替换类Object
的对象,整个表达式的类型是Object
(JLS:"如果是第二个和第三个中的一个操作数是null类型而另一个的类型是引用类型,那么条件表达式的类型就是引用类型。")。
在表达式的类型评估之后,方法选择符合预期。
您提供的if
示例不同:您使用两个不同类型的对象调用方法。三元条件运算符始终在编译时中被评估为一个类型,适合这两个术语。
答案 1 :(得分:2)
test((10%2==0)?null:new Object());
与:
相同Object o;
if(10%2==0)
o=null;
else
o=new Object();
test(o);
由于o
的类型为Object
(就像(10%2==0)?null:new Object()
的类型)test(Object)
将始终被调用。 o
的价值并不重要。
答案 2 :(得分:2)
JLS 15.25:
条件表达式的类型确定如下:
[...]
- 如果第二个和第三个操作数之一是null类型和另一个操作数的类型 是一个引用类型,然后条件表达式的类型就是引用 类型。
[...]
所以
的类型10 % 2 == 0 ? null : new Object();
是对象。
答案 3 :(得分:1)
您的答案是:运行时,因为在运行时指定参数是String的实例,所以在编译时无法找到它。
答案 4 :(得分:1)
这是一个非常好的问题。
让我试着澄清你上面写的代码。
试验(空);
在此,null
将被转换为字符串类型,因此调用test(String obj)
,根据JLS,您可以通过调用确信。
test((10%2 == 0)?null:new Object());
将返回布尔值“true”值。因此,第一个布尔“true”值将自动转换为Boolean Wrapper类对象。布尔包装器对象在三元运算符中找到与new Object()
选项的最佳匹配。并且该方法使用Object作为参数调用,因此它调用以下方法
public static void test(Object obj)
为了实验,您可以尝试以下组合,然后您将获得更好的清晰度。
test((10%2 == 0)?new Object():“stringObj”);
test((10%2 == 0)?new Object():null);
test((10%2 == 0)?“stringObj”:null);
试验((10%2 == 0)空:NULL);
这次又以布尔“true”值的形式返回,并且它将再次遵循与上面解释的相同的强制转换。但是这次你的三元运算符中没有new Object()
参数。因此它将自动类型转换为null
对象。它再次遵循与您的第一个方法调用相同的方法调用。
if .. else
语句,那么在您要求代码的最后一次。然后编译器也用代码做公平的决定。 if(10%2 == 0){ 试验(NULL); }
此处if条件为true并且调用此代码test(null)
。因此,它始终使用String作为参数调用第一个test(String obj)
方法,如上所述。
答案 5 :(得分:0)
我认为你的问题是你做出了错误的假设,你的表达方式:
test((10%2==0)?null:new Object());
和
test((10%2==0)?null:null;
将始终调用test(null),这就是他们将通过test(Object)的原因。
答案 6 :(得分:0)
正如@Banthar所提到的,?:
运算符首先为变量赋值,然后计算条件。
另一方面,您提到的if
条件总是返回true,因此编译器将仅使用if-else
的主体替换整个if
块。
答案 7 :(得分:0)
1)test()
方法由编译时参数的类型决定:
test((Object) null);
test((Object)"String");
输出:
Object called
Object called
2)编译器更智能,编译后的代码仅相当于:
test(null);
您可以使用javap -c
检查字节码:
0: aconst_null
1: invokestatic #6 // Method test:(Ljava/lang/String;)V
4: return
答案 8 :(得分:0)
这是Java Language Specifications对此问题所说的内容。
如果多个方法声明都可访问且适用 要进行方法调用,有必要选择一个来提供 运行时方法调度的描述符。 Java编程 language使用选择最具体方法的规则。
这是你的情况下的test(String)方法。
因此,如果你加上......
public static void test(Integer obj){
System.out.println("Ingeter called");
}
它将显示编译错误 - 方法test(String)对于OverloadingTest类型是不明确的。
就像JLS说:
可能没有方法是最具体的,因为有 两种或更多种最具体的方法。在这种情况下:
如果所有最大特定方法具有相同的签名,则: 如果其中一个最大特定方法未声明为abstract,则为 是最具体的方法。否则,所有最大的具体 方法必然被宣布为抽象的。最具体的方法是 在最大特定方法中任意选择。然而 如果和,则认为最具体的方法抛出一个已检查的异常 只有在每个的throws子句中声明了该异常时 最具体的方法。否则,我们说的方法 调用不明确,发生编译时错误。