黑马程序员技术交流社区

标题: 重载函数,参数为null时,调用的处理(精确性原则) [打印本页]

作者: OCTSJimmy    时间: 2014-11-7 09:28
标题: 重载函数,参数为null时,调用的处理(精确性原则)
本帖最后由 OCTSJimmy 于 2014-11-7 09:29 编辑

感谢这么多朋友支持我的前一篇帖子《这里有两道题,都错了的话视频可以重看了》(链接:http://bbs.itheima.com/thread-151079-1-1.html
关于该文中的第一道题,很多朋友一直疑惑,为什么它调用的是以数组为参数的构造函数,却并不是Object为参数的构造函数呢。这里特别说明一下这个问题。
关于以下程序的输出结果
  1. public class TestNull {
  2. public void show(String a){
  3. System.out.println("String");
  4. }
  5. public void show(Object o){
  6. System.out.println("Object");
  7. }
  8. public static void main(String args[]){
  9. TestMain t = new TestMain();
  10. t.show(null);
  11. }
  12. }
复制代码
结果是: String
解释(主要是重载函数调用时精确性的问题)
《java解惑》

谜题46:令人混淆的构造器案例
本谜题呈现给你了两个容易令人混淆的构造器。main方法调用了一个构造器,但是它调用的到底是哪一个呢?该程序的输出取决于这个问题的答案。那么它到底会打印出什么呢?甚至它是否是合法的呢?
  1. public class Confusing {
  2. private Confusing(Object o) {
  3. System.out.println("Object");
  4. }
  5. private Confusing(double[] dArray) {
  6. System.out.println("double array");
  7. }
  8. public static void main(String[] args) {
  9. new Confusing(null);
  10. }
  11. }
复制代码
   传递给构造器的参数是一个空的对象引用,因此,初看起来,该程序好像应该调用参数类型为Object的重载版本,并且将打印出Object。
    另一方面,数组也是引用类型,因此null也可以应用于类型为double[ ]的重载版本。
    你由此可能会得出结论:这个调用是模棱两可的,该程序应该不能编译。
    如果你试着去运行该程序,就会发现这些直观感觉都是不对的:该程序打印的是double array。
    这种行为可能显得有悖常理,但是有一个很好的理由可以解释它。
    Java的重载解析过程是以两阶段运行的。
    第一阶段:选取所有可获得并且可应用的方法或构造器。
    第二阶段:在第一阶段选取的方法或构造器中选取最精确的一个如果一个方法或构造器可以接受传递给另一个方法或构造器的任何参数那么我们就说第一个方法比第二个方法缺乏精确性[JLS 15.12.2.5]。
    在我们的程序中,两个构造器都是可获得并且可应用的。
    构造器Confusing(Object)可以接受任何传递给Confusing(double[ ])的参数,因此Confusing(Object)相对缺乏精确性。
  (每一个double数组都是一个Object,但是每一个Object并不一定是一个double数组。)
   因此,最精确的构造器就是Confusing(double[ ]),这也就解释了为什么程序会产生这样的输出。

   如果你传递的是一个double[ ]类型的值,那么这种行为是有意义的;但是如果你传递的是null,这种行为就有违直觉了。
   理解本谜题的关键在于在测试哪一个方法或构造器最精确时,这些测试没有使用实际的参数:即出现在调用中的参数。
   这些参数只是被用来确定哪一个重载版本是可应用的。
   一旦编译器确定了哪些重载版本是可获得且可应用的,它就会选择最精确的一个重载版本,而此时使用的仅仅是形式参数:即出现在声明中的参数。

  要想用一个null参数来调用 Confusing(Object)构造器,你需要这样写代码:new Confusing((Object)null)。
  这可以确保只有Confusing(Object)是可应用的。
  更一般地讲,要想强制要求编译器选择一个精确的重载版本,需要将实际的参数转型为形式参数所声明的类型。

  以这种方式来在多个重载版本中进行选择是相当令人不快的。
  在你的API 中,应该确保不会让客户端走这种极端。
  理想状态下,你应该避免使用重载:为不同的方法取不同的名称。
  当然,有时候这无法实现,例如,构造器就没有名称,因而也就无法被赋予不同的名称。
  然而,你可以通过将构造器设置为私有的并提供公有的静态工厂,以此来缓解这个问题[EJ Item 1]。
  如果构造器有许多参数,你可以用Builder模式[Gamma95]来减少对重载版本的需求量。

  如果你确实进行了重载,那么请确保所有的重载版本所接受的参数类型都互不兼容,这样,任何两个重载版本都不会同时是可应用的。
  如果做不到这一点,那么就请确保所有可应用的重载版本都具有相同的行为[EJ Item 26]。

  总之,重载版本的解析可能会产生混淆。应该尽可能地避免重载,如果你必须进行重载,那么你必须遵守上述方针,以最小化这种混淆。
  如果一个设计糟糕的API强制你在不同的重载版本之间进行选择,那么请将实际的参数转型为你希望调用的重载版本的形式参数所具有的类型。

  举一反三:
  1. class Parent1 extends Object{
  2. public Parent1(){
  3.   System.out.println("空参数");
  4. }
  5. public Parent1(Son1 obj){
  6.   System.out.println("Son1");
  7. }
  8. public Parent1(Parent1 par){
  9.   System.out.println("Parent1");
  10. }
  11. public Parent1(S s){
  12.   System.out.println("S");
  13. }
  14. public Parent1(Object str){
  15.   System.out.println("Object");
  16. }
  17. }
  18. class Son1 extends Parent1{
  19. }
  20. class S extends Son1{
  21. }
  22. public class AreYouSure
  23. {
  24.     public static void main(String[] args)
  25.     {
  26.         new Parent1(null);
  27.     }
  28. }
复制代码

请回答以上代码的运行结果是什么?

答案:



作者: 李天富    时间: 2014-11-7 16:56
是s吧?

作者: RINGARES    时间: 2014-11-7 22:37
应该是s
ps.把这个帖子的连接放到之前的贴子,让更多同学看到详细的解释吧~~~~
作者: 爽朗的菜籽    时间: 2014-11-9 17:37
是s吧,求楼主不要刺激我
作者: 冥夜    时间: 2014-11-9 18:09
好乱啊- -眼都花了
作者: 空洞的望着窗外    时间: 2014-11-9 20:58
S,还是先用小的,嘿嘿。。挺好玩的,这应该是动态绑定机制。
作者: 王世虎    时间: 2014-11-10 12:12
我都快懵了,最精确的那个,是不是最小的最子类的?s
作者: 执着的桶牛    时间: 2014-12-1 17:58
马上转来了这里,看看答案,感觉这次应该不会错了
作者: as604049322    时间: 2014-12-1 19:27
搞不清楚,应该是S吧。
作者: 十万一千    时间: 2014-12-1 19:51
本帖最后由 十万一千 于 2014-12-1 19:54 编辑

类的继承链条[Object<--Parent1<--Son1<--S]  按照上面的理论,某类子类的对象可以看做其父类的对象,反过来不可以的话,楼上的回答就是对的。如果在给Parent1添加一个构造器:
  1.         public Parent1(String string){
  2.                 System.out.println("string");
  3.         }
复制代码
会打印什么?  或者编译错误或运行时异常?

作者: 天天小志    时间: 2014-12-1 23:19
怎么老是回答了才能看啊!
作者: rel4x    时间: 2014-12-2 10:14
学习学习了
作者: 知足    时间: 2014-12-2 11:11
好东西!
作者: chudaming213    时间: 2014-12-6 15:25
看看是s吧
作者: chudaming213    时间: 2014-12-6 15:27
长见识了,谢谢楼主,欢迎今后继续考:handshake
作者: DuckJava    时间: 2014-12-6 22:12
楼主是个很有意思的人
作者: DuckJava    时间: 2014-12-6 22:14
好吧,请不要怪我这么冲动

1.jpg (46.39 KB, 下载次数: 139)

1.jpg

作者: xiaoxiyang    时间: 2014-12-6 22:28
跟帖看下结果,S。
作者: lwh316658735    时间: 2014-12-6 22:51
看看!。
作者: 对牛弹吉他    时间: 2014-12-7 19:52
要重看的节奏
作者: lhyzxc0205    时间: 2014-12-8 08:49
看看答案
作者: Mydream524    时间: 2014-12-15 23:34
我值看看   不说话~~~
作者: lijingbo    时间: 2014-12-21 09:55
Parent1是这个吧?
作者: 斷灬dian    时间: 2014-12-23 19:13
学到了 多谢
作者: 斷灬dian    时间: 2014-12-26 11:33
来学学。。。。
作者: c91764000    时间: 2014-12-26 21:04
这不错来研究一下!
作者: 白月留梦    时间: 2015-6-7 11:17
应该是s




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2