方法重载并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);
由于o
types是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。 否则,我们说方法调用是不明确的,并发生编译时错误。