JAVA 重载方法,参数为NULL时,调用的处理 (精确性原则)

引子:大家可以思考一下下面程序的输出结果

public class TestNull {
    public void show(String a){
        System.out.println("String");
    }
    public void show(Object o){
        System.out.println("Object");
    }
    public static void main(String args[]){
        TestMain t = new TestMain();
        t.show(null);
    }
}  

运行的结果是:

String

解释(主要是重载函数调用时精确性的问题):
《java解惑》这本书谜题46解释了这种情况。下面内容摘自《Java解惑》

谜题46:令人混淆的构造器案例
本谜题呈现给你了两个容易令人混淆的构造器。main方法调用了一个构造器,但是它调用的到底是哪一个呢?该程序的输出取决于这个问题的答案。那么它到底会打印出什么呢?甚至它是否是合法的呢?

public class Confusing {
      private Confusing(Object o) {
          System.out.println("Object");
      }
      private Confusing(double[] dArray) {
         System.out.println("double array");
      }
      public static void main(String[] args) {
          new Confusing(null);
      }
}

传递给构造器的参数是一个空的对象的引用,因此,初看起来,改程序好像应该调用参数类型为Object的重载版本,并且将打印出Object。另一方面,数组也是引用类型,因此Null也可以应用于类型是double[]的重载版本。由此可能会得出结论:

  这个调用是模棱两可的,改程序应该是不能编译的

如果你试着去运行程序,就会发现我之前的直观猜测是不对的:该程序打印 的是

double array

这种行为有悖常理,但是有一个很好的理由可以解释它,

一:选取所有可获得的并可以应用的构造器或方法

二:在第一步选取的方法或者构造器中选择最精确的一个 ,二第二个方法就是缺乏精确性

  在我们的程序中,两个构造器都是可获得 并且可应用的。构造器Confusing(Object)可以接受任何传递给Confusing(double[ ])的参数,因此Confusing(Object)相对缺乏精确性。(每一个double数组都是一个Object,但是每一个Object并不一定是 一个double数组。)因此,最精确的构造器就是Confusing(double[ ]),这也就解释了为什么程序会产生这样的输出。

理解本谜题的关键在于在测试哪一个方法或构造器最精确时,这些测试没有使用实际的参数:即出现在调用中的参数。这些参数只是被用来确定哪一个重载版本是可应用的。一旦编译器确定了哪些重载版本是可获得且可应用的,它就会选择最精确的一个重载版本,而此时使用的仅仅是形式参数:即出现在声明中的参数。

要想用一个null参数来调用Confusing(Object)构造器,你需要这样写代码:

以这种方式来在多个重载版本中进行选择是相当令人不快的。在你的API 中,应该确保不会让客户端走这种极端。理想状态下,你应该避免使用重载:为不同的方法取不同的名称。当然,有时候这无法实现,例如,构造器就没有名称,因 而也就无法被赋予不同的名称。然而,你可以通过将构造器设置为私有的并提供公有的静态工厂,以此来缓解这个问题[EJ Item 1]。如果构造器有许多参数,你可以用Builder模式[Gamma95]来减少对重载版本的需求量。
如果你确实进行了重载,那么请确保所有的重载版本所接受的参数类型都互不兼容,这样,任何两个重载版本都不会同时是可应用的。如果做不到这一点,那么就请确保所有可应用的重载版本都具有相同的行为[EJ Item 26]。
总之,重载版本的解析可能会产生混淆。应该尽可能地避免重载,如果你必须进行重载,那么你必须遵守上述方针,以最小化这种混淆。如果一个设计糟糕的API强制你在不同的重载版本之间进行选择,那么请将实际的参数转型为你希望调用的重载版本的形式参数所具有的类型。

参考文档:https://blog.csdn.net/liusrblog/article/details/8088305

原文地址:https://www.cnblogs.com/revel171226/p/9426078.html

时间: 2024-08-29 16:11:21

JAVA 重载方法,参数为NULL时,调用的处理 (精确性原则)的相关文章

Java的方法参数-想想挺有趣的问题

一直认为Java的方法参数都是传递值,调用后对本身不影响. Java不存在C/C++中的指针,在快速排序中,传入的数组,却发生了值的改变.由此引发的思考: //为方便举例,以下为部分快速排序伪代码 传入的数组,在递归中,数组值被操作. void quickSort(int s[], int l, int r){ if (l < r){ quickSort(s, l, i - 1); quickSort(s, i + 1, r); } } 结论:Java方法参数中传递的是值,在参数是引用类型(如数

JAVA获取方法参数名的分析(一)

关于题目 首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数.但有一个东西反射取不到,那就是我们对方法传入参数的命名. 取得传入参数的名字有什么意义? 对这个问题的探究,源于在写一个测试类时候的需求.假设我们有一个类需要测试,这个类中有数十个方法.为每个方法编写测试类,将耗费大量的时间和精力.因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数

java 方法参数-值调用,引用调用问题

(博客内容来自于core java卷一) 1. xx调用:程序设计语言中方法参数的传递方式: 引用调用(call by reference):表示方法接收的是调用者提供的变量地址. 值调用(call by value):表示方法接收的是调用者提供的值. 命名调用(call by name):已经成为历史. 2. Java使用值调用,而且只有值调用.也就是说方法得到的是参数值的一个拷贝,并不是参数值本身,所以,方法不能修改传递给它的的任何参数变量本身. 看下面代码: public class te

黑马程序员-java中方法的知识点和递归调用

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- 方法执行原理:(方法不调用不执行,调用才执行)  *   方法在调用的这时候,才会在内存中划分空间  *   方法在调用的时候,是在栈内存中分配的空间(jvm有一块内存是栈内存) * 函数/方法目的: 还是为了代码重用  * 相同的类型的

java中方法的知识点和递归调用

 方法执行原理:(方法不调用不执行,调用才执行) *   方法在调用的这时候,才会在内存中划分空间 *   方法在调用的时候,是在栈内存中分配的空间(jvm有一块内存是栈内存) * 函数/方法目的: 还是为了代码重用 * 相同的类型的操作,不容重复的写代码 * 函数/方法:就是一段有名字的代码段 * 方法的定义 *  [方法修饰符列表] 方法返回值类型 方法名(参数1,参数2,参数3....){ *          执行语句; *  } * *  方法修饰符列表是可选的:权限控制的:publi

Java Map释放内存置null以及调用clear()的区别

今天自己在总结map的时候,想到了在释放Map对象空间的时候就有使用过将Map对象置null,也有时候会调用clear()将Map中的数据清除,那么它们都有什么区别呢? Map<Integer, String> map = new HashMap<>(); 首先,在创建一个map对象时,map指向堆中新创建的对象,这时候的map是一个没有key和value的空对象.众所周知, map.hashCode()某种意义上相当返回了对象的地址.所以在用刚创建的map对象调用hashCode

WebBrowser控件的NavigateToString()方法 参数 为中文时乱码问题的解决。

public static string ConvertExtendedASCII(string HTML) { StringBuilder str = new StringBuilder(); char c; for (int i = 0; i < HTML.Length; i++) { c = HTML[i]; if (Convert.ToInt32(c) > 127) { str.Append("&#" + Convert.ToInt32(c) + "

JAVA入门学习: 方法参数的传递(函数传参问题)

引言:我们知道C++中拥有两种参数传递方式, 值调用和引用调用. 有些程序员认为JAVA程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的. 由于这种误解存在普遍性,所以下面将阐述一下这个问题. 所以用一段简单的代码来阐述一下这个问题: 1 //如果方法参数是的输入时引用,那么我们将交换两个指针(即引用) 2 public static void swap(Employee x, Employee y) 3 { 4 Employee temp = x; 5 x =y; 6 y = te

Java重载重写与实现方法的规则

这几天在实训过程中做到了一个题,这个题目问的是 非抽象类实现接口后,必须实现接口中的所有抽象方法,除了abstract外,方法头必须完全一致.这句话是错误的.然后在做错以后自己总结一下重载 重写 和实现的几个点. 重载:方法名一致即可,访问修饰符.返回值类型无关,然后不同的重载方法参数列表的参数的顺序,类型,数量进行变换即可. 重写:重写方法的访问修饰符可以不同,但访问权限需要比父类中的大,比如protected修饰的方法重写的时候可以重写为public类型.返回值类型可以是父类中方法返回值类型