类与接口(四)方法重载解析
###一、方法重載簡介
方法重載: 當兩個(或多個)方法的名稱相同,而參數的對應類型或個數不同時,我們就說方法重載了。當然,編譯器也能識別出來。
編譯器是如何識別調用了哪個方法?
在往下講前,我們先來了解一下:編譯器是怎么才能識別出程序調用了那個方法。其實,這個問題就是在問:在調用方法處,編譯器能得到調用方法的什么信息,從而能找到對應的方法?我們一般的方法調用是這樣的:
method( vars );也就是說,方法調用處,一共為編譯器提供兩個信息:方法名、參數列表。
所以,編譯器只能通過 方法名 和 參數列表 來識別調用方法。
有一道面試題問:為什么不能通過返回類型來重載方法?
就是上面所說的,方法調用處并沒有提供返回類型的信息,所以當多個方法只有返回類型不一樣時,編譯器就不知道調用了那個方法了。
我們已經知道了編譯器是怎么識別方法的了,而對于方法重載,其要求方法名是一樣的,那么我們只需要關注 參數列表 便可以了。參數列表區分,或者說重載方法的區分:
- 參數的個數
- 參數的類型
- 參數的順序
###二、方法重載的匹配選擇
方法重載后,方法調用處可能會遇到應該選擇哪個重載方法的問題,如果只有唯一個重載方法可以匹配,那么就沒問題。然而,大部分情況卻是有多個重載方法是可以匹配的,那么這時候就應該選擇最合適的重載方法.
匹配最合適、最明確的重載方法,其實就是實參列表去匹配當前重載方法中形參列表,尋找與實參列表最相近的形參列表
##1、基本類型之間的重載
對于基本類型來說,從“短類型”擴展成“長類型”是默認允許、自動進行的,這就可能造成了實參可能匹配到多個“長類型”的形參,看個簡單例子:
public static void main(String[] args) {short s = 4;m(s); }public static void m(int x){//方法一System.out.println("重載方法一"); }public static void m(float x){//方法二System.out.println("重載方法二"); }運行結果:
重載方法一
short類型 可以默認自動轉換成int、'float’類型。但m(s)真正匹配選擇的是m(int x)方法,而不是形參長度更長的m(float x)。所以可以看出,基本類型的形參匹配規則是: 如果沒有匹配到精確類型的形參,則優先匹配 存儲長度(范圍)大于且是最接近實參的存儲長度的形參,從而確定調用哪個重載方法
##2、引用類型間的重載
對于引用類型來說,可以匹配到多個重載方法的原因是:引用類型的對象進行類型上轉也是JVM默認自動進行的,那么就可能匹配多個祖先類型的形參看下面的例子:
public class Test_3 {public static void main(String[] args) {Children children = new Children();someMethod(children); } public static void someMethod(Ancestor an) {//重載方法1System.out.println("this is Ancestor Method!"); }public static void someMethod(Parent an) {//重載方法2System.out.println("this is Parent Method!"); } }//3個具有繼承關系的類 class Ancestor{//祖先類 }class Parent extends Ancestor{//父類,繼承于Ancestor }class Children extends Parent{//子類,繼承于Parent }運行結果:
this is Parent Method!
可以看出,引用類型與基本類型一樣,都是選擇”最明確的方法“, 引用類型間選擇最明確的重載方法的規則是: 如果找不到重載方法的形參的引用類型與實參一致,則實參優先匹配 在繼承樹結構上,離實參類型最近的形參,則此形參所在的重載方法便是最明確的重載方法。
##3、自動裝箱拆箱、可變參數類型
裝箱拆箱、以及可變參數列表的處理都是由編譯器自動處理,也就是說是默認自動進行的,這同樣會讓實參列表可以匹配多個形參列表 ,可以匹配多個重載方法。
此小節將會涉及到基本類型、引用類型、自動裝箱拆箱可變參數的重載方法匹配的優先級。
看下面的例子,這個例子包括很多情況:
public class Test_3 {public static void main(String[] args) {short s = 5; overloadMethod(s);// test1Integer i = 10;overloadMethod(i);//test2overloadMethod(s,s);//test3 } public static void overloadMethod(int a) { //m1System.out.println("調用 overloadMethod(int)"); }public static void overloadMethod(Short in) {//m2System.out.println("調用 overloadMethod(short)"); }public static void overloadMethod(int a,int b) {//m3System.out.println("調用 overloadMethod(int,int)"); }public static void overloadMethod(short... s) { //m4System.out.println("調用 overloadMethod(short...)"); }public static void overloadMethod(Integer... i) {//m5System.out.println("調用 overloadMethod(Integer...)"); } }運行結果
調用 overloadMethod(int)
調用 overloadMethod(int)
調用 overloadMethod(int,int)
我們來分析一下上面的例子中,方法調用處可以匹配到的方法:
- test1 處的方法調用可以匹配的重載方法有:m1(基本類型的短類型自動轉為長類型)、m2(自動裝箱)、m4(可變參數列表)
- test2 處的方法調用可以匹配的重載方法有:m1(自動拆箱)、m5(可變參數列表);
- test3 處的方法調用可以匹配的重載方法有:m3(基本類型的短類型自動轉換成長類型)、m4(可變參數列表)
查看輸出結果,發現:test1處選擇了m1、test2選擇了m1,test3選擇了m3。
根據這樣的結果,也就是這幾種形參匹配規則還是有個匹配的順序的。對重載方法的選擇作以下總結:
- 先按照實參的類型(基本類型或引用類型)對應匹配規則,進行查找最相近的形參列表,從而找到最明確的重載方法;找不到,則執行第二步;
- 對實參進行裝箱或拆箱轉換(前提是實參是基本類型或者是包裝類),再安按照轉換得到的類型進行匹配形參的類型(形參類型與轉換類型要一致,特別注意基本類型);找不到,則執行第三步;
- 匹配形參是可變參數的重載方法,此時,形參的類型可以是 實參的類型以及通過 基本類型的短轉長、自動裝箱拆箱、祖先類型 得到的轉換類型。
將上面的總結再簡化一下,可以簡化成 重載方法的形參匹配規則的優先級:
當前類型(基本類型或引用類型)的匹配規則 > 自動裝箱拆箱 > 可變參數列表
再看一個例子:
public class MyTest {public static void main(String[] args) {int a = 5;short s = 8;m(a,s);}public static void m(int a,Short b) {//m1System.out.println("調用了m(int,Short)"); }public static void m(float f,short s) {//m2System.out.println("調用了m(float,short)"); } }運行結果:
調用了m(float,short)
分析: 實參都是基本類型,優先考慮形參列表都是基本類型的重載方法,找不到才考慮自動裝箱拆箱
##4、泛型方法的重載
泛型方法的重載規則: 將泛型方法的類型變量擦除,然后與非泛型方法一樣,按照上面所說的三種規則一一匹配
public static void main(String[] args) { //創建Runnable對象 Runnable r = new Runnable() { public void run(){} }; //調用泛型方法m(r); }public static <T> void m(T t) {//m1System.out.println("調用了<T> void m(T)"); }public static <T extends Runnable> void m(T t) {//m2System.out.println("調用了<T extends Runnable> void m(T t)"); }運行結果:
調用了 void m(T t)
上面的兩個泛型方法m(T t)進行類型擦除后是:
public static void m(Object t);public static void m(Runnable t);顯然,調用方法應該是m2,與運行結果相符;
##5. 沒法確定的重載方法調用
盡管編譯器會按照上面所說的三種優先級別去讓實參匹配形參,然而匹配的結果卻不一定是唯一的,也就是說會匹配到多個方法,從而無法確定調用那個方法,編譯失敗
情況一: 實參列表的所有最佳匹配的形參不在同一個方法中
public class MyTest {public static void main(String[] args) {int aa = 5;short ss = 8;m(aa,ss);//編譯不通過,無法確定調用了那個重載方法}public static void m(int a,double b) {//m1System.out.println("調用了m(int,Short)"); }public static void m(float f,int c) {//m2System.out.println("調用了m(float,short)"); } }分析:
m(aa,ss)的調用編譯失敗,因為實參aa的最佳匹配m(int,double)的第一個形參,而實參ss的最佳匹配則是m(float,short)的第二個形參。
因此,實參列表的(aa,ss)的最佳形參類型匹配分開在了兩個重載方法中。
注意一下,即使某個重載方法的形參列表包含最多的最相近的形參類型,只要不是全部,那么依舊無法確定調用了哪個重載方法。
情況二: 可變參數列表的特殊性 – 無法根據可變參數的類型來重載方法
public static void m(short... s) {}public static void m(Short... s) {}public static void m(int... s) {}調用測試例子:
short s = 8; Short sl = 10; m(s,s);//編譯不通過 m(s,sl);//編譯不通過 m(sl,sl);//編譯不通過##重寫 與 重載的區別
- 重寫是針對父類與子類間的方法,即必須先得繼承父類的方法。而重載則沒有這種限制。
- 重寫要求方法的 而方法重載則只需要 方法名相同,參數列表不同就行了。
- 方法重載時,方法的調用是在編譯時期就已經確定了調用那個方法;方法重寫,則要在運行時,才能確定調用的是子類還是父類的方法。
作者:jinggod
出處:http://www.cnblogs.com/jinggod/p/8503150.html
總結
以上是生活随笔為你收集整理的类与接口(四)方法重载解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类与接口(三)java中的接口与嵌套接口
- 下一篇: 类与接口(五)java多态、方法重写、隐