方法重载并select最具体的types

示例代码是:

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); } 

输出是:

string调用
10%2 == 0是真的
调用的对象
string调用

第一个调用test(null)的方法用String参数调用,根据The Java Language Specification可以理解。

1)任何人都可以解释我在前面的调用中调用test()依据是什么?

2)当我们再次说, if条件:

  if(10%2==0){ test(null); } else { test(new Object()); } 

它始终使用String参数调用该方法。

编译器会在编译时计算expression式(10%2)吗? 我想知道expression式是在编译时还是运行时计算的。 谢谢。

Java使用早期绑定。 最具体的方法是在编译时select的。 最具体的方法是通过参数的数量和参数的types来select的。 参数的数量在这种情况下是不相关的。 这给我们留下了参数的types。

什么types的参数? 两个参数都是expression式,使用三元条件运算符。 问题归结为:有条件的三元运算符返回什么types? types是在编译时计算的。

鉴于这两个expression式:

 (10%2==0)? null : new Object(); // A (10%2==0)? null : null; // B 

这里列出了types评估的规则。 在B ,很容易,两个条件是完全相同的:返回null ( 无论是哪种types)(JLS:“如果第二个和第三个操作数具有相同的types(可能是空types),那么条件expression式的types“)。 第二项是来自某个特定的课程。 由于这是更具体的, null可以replaceObject类的Object ,整个expression式的types是Object (JLS:“如果第二个和第三个操作数中的一个是nulltypes而另一个types是引用types,那么条件expression式的types就是引用types“)。

在expression式的types评估之后,方法select如预期的那样。

if你给的例子是不同的:你用两种不同types的对象调用方法。 三进制条件运算符总是在编译时被评估为一种types,以适应这两个术语。

 test((10%2==0)?null:new Object()); 

是相同的:

 Object o; if(10%2==0) o=null; else o=new Object(); test(o); 

由于otypes是Object (就像(10%2==0)?null:new Object()test(Object)将始终被调用。 o的价值并不重要。

JLS 15.25:

条件expression式的types如下确定:

[…]

  • 如果第二个和第三个操作数中的一个是空types,而另一个的types是引用types,那么条件expression式的types就是该引用types。

[…]

所以这种types

 10 % 2 == 0 ? null : new Object(); 

是对象。

你的答案是: 运行时,因为在运行时指定的参数是否是String的实例,所以在编译时不能find这个。

这是一个非常好的问题。

让我试着澄清你上面写的代码。

  • 在你的第一个方法调用

试验(NULL);

在这个null将转换成stringtypes,所以调用test(String obj) ,每个JLS你相信与调用。

  • 在第二个方法调用

testing((10%2 == 0)?null:new Object());

哪个将返回布尔值“真”值。 所以第一个布尔值“true”的值将自动转换为布尔Wrapper类对象。 布尔包装对象在三元运算符中find与new Object()选项的最佳匹配。 该方法调用Object作为参数,所以它调用以下方法

public static void test(Object obj)

为了实验起见,你可以尝试下面的组合,那么你会得到更好的清晰度。

testing((10%2 == 0)?new Object():“stringObj”);

testing((10%2 == 0)?new Object():null);

testing((10%2 == 0)?“stringObj”:null);

  • 最后在最后的时候用下面的代码调用。

试验(?(10%2 == 0)空:NULL);

这次它又以布尔“真”值的forms返回,它将再次按照上面所述的相同的强制转换。 但是这次在你的三元运算符中没有new Object()参数。 所以它会被自动types转换为null对象。 同样的方法调用跟第一个方法调用一样。

  • 在最后的时候,如果你把if .. else语句放在代码中if .. else 然后编译器也会根据代码做出公平的决定。

如果(10%2 == 0){test(null); }

在这里所有的时候你的条件是真的,并调用这个代码test(null) 。 因此,它总是调用String作为参数的第一个test(String obj)方法,如上所述。

我认为你的问题是你正在做出错误的假设,你的表情:

 test((10%2==0)?null:new Object()); 

 test((10%2==0)?null:null; 

总是会调用testing(null),这就是为什么他们会通过testing(对象)。

正如@Banthar提到的那样?:运算符首先为variables赋值,然后评估条件。 另一方面,你提到的if条件总是返回true,所以编译器将只用if-else的主体replace整个if-else块。

1) test()方法由编译时的参数types决定:

 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 

这是Java语言规范对这个问题的看法。

如果多个方法声明都可以访问并适用于方法调用,则需要select一个方法声明来提供运行时方法调度的描述符。 Java编程语言使用最具体的方法select的规则。

这是你的情况下的testing(string)方法。

因为如果你添加…

 public static void test(Integer obj){ System.out.println("Ingeter called"); } 

它会显示编译错误 – 方法testing(String)对于OverloadingTesttypes是不明确的。

就像JLS所说:

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

如果所有最具体的方法都具有相同的签名,那么:如果其中一个最具体的方法没有被声明为抽象的,那么它是最具体的方法。 否则,所有最具体的方法都必须声明为抽象的。 最具体的方法是在最具体的方法中任意select的。 但是,当且仅当在每个最大特定方法的throws子句中声明该exception时,才会考虑最具体的方法抛出checkedexception。 否则,我们说方法调用是不明确的,并发生编译时错误。