Java方法的簽名是否包含返回類型?
- java
1.概述
方法簽名只是Java中整個方法定義的子集。因此,簽名的確切解剖結構可能引起混亂。
在本教程中,我們將學習方法簽名的元素及其在Java編程中的含義。
2.方法簽名
Java中的方法支持重載,這意味著可以在相同的類或類層次結構中定義多個具有相同名稱的方法。因此,編譯器必須能夠靜態綁定客戶端代碼所引用的方法。因此,方法簽名唯一地標識每個方法。
根據Oracle的說法,方法簽名由名稱和參數類型組成。因此,方法聲明的所有其他元素(例如修飾符,返回類型,參數名稱,異常列表和主體)都不是簽名的一部分。
讓我們仔細研究方法重載及其與方法簽名的關係。
3.重載錯誤
讓我們考慮以下代碼
:
public void print() {
System.out.println("Signature is: print()");
}
public void print(int parameter) {
System.out.println("Signature is: print(int)");
}
如我們所見,由於方法具有不同的參數類型列表,因此代碼會編譯。實際上,編譯器可以確定性地將任何調用綁定到一個或另一個。
現在,通過添加以下方法來測試重載是否合法:
public int print() {
System.out.println("Signature is: print()");
return 0;
}
編譯時,出現“類中已定義方法”錯誤。證明方法返回類型不是方法簽名的一部分。
讓我們嘗試使用修飾符:
private final void print() {
System.out.println("Signature is: print()");
}
我們仍然看到相同的“方法已經在類中定義”錯誤。因此,方法簽名不依賴於修飾符。
通過更改拋出的異常來重載可以通過添加以下內容進行測試:
public void print() throws IllegalStateException {
System.out.println("Signature is: print()");
throw new IllegalStateException();
}
再次,我們看到“方法已經在類中定義”錯誤,表明throw聲明不能成為簽名的一部分。
我們可以測試的最後一件事是更改參數名稱是否允許重載。讓我們添加以下方法:
public void print(int anotherParameter) {
System.out.println("Signature is: print(int)");
}
如預期的那樣,我們得到相同的編譯錯誤。這意味著參數名稱不會影響方法簽名。
3.泛型和類型擦除
使用通用參數,類型擦除會更改有效簽名。實際上,它可能與使用通用類型上限而不是通用令牌的另一種方法發生衝突。
讓我們考慮以下代碼:
public class OverloadingErrors<T extends Serializable> {
public void printElement(T t) {
System.out.println("Signature is: printElement(T)");
}
public void printElement(Serializable o) {
System.out.println("Signature is: printElement(Serializable)");
}
}
即使簽名看起來不同,類型刪除後,編譯器也無法靜態綁定正確的方法。
由於類型擦除Serializable,
我們可以看到編譯器將T
替換為上限Serializable。因此,它與使用Serializable
顯式地衝突。
當泛型類型沒有邊界時,我們將在基本類型Object
4.參數列表和多態
方法簽名考慮了確切的類型。這意味著我們可以重載其參數類型為子類或超類的方法。
但是,我們必須特別注意,因為靜態綁定將嘗試使用多態性,自動裝箱和類型提升進行匹配。
讓我們看下面的代碼:
public Number sum(Integer term1, Integer term2) {
System.out.println("Adding integers");
return term1 + term2;
}
public Number sum(Number term1, Number term2) {
System.out.println("Adding numbers");
return term1.doubleValue() + term2.doubleValue();
}
public Number sum(Object term1, Object term2) {
System.out.println("Adding objects");
return term1.hashCode() + term2.hashCode();
}
上面的代碼完全合法,可以編譯。調用這些方法時可能會引起混亂,因為我們不僅需要知道所調用的確切方法簽名,而且還需要Java如何根據實際值靜態綁定。
讓我們探索一些最終綁定到sum(Integer, Integer)
方法調用:
StaticBinding obj = new StaticBinding();
obj.sum(Integer.valueOf(2), Integer.valueOf(3));
obj.sum(2, 3);
obj.sum(2, 0x1);
對於第一個調用,我們具有確切的參數類型Integer, Integer.
在第二次調用中,Java將為我們自動將int
裝箱為Integer
.
最後,Java將0x1
轉換為int
,然後將其自動裝箱為Integer.
同樣,我們有以下綁定到sum(Number, Number)
調用:
obj.sum(2.0d, 3.0d);
obj.sum(Float.valueOf(2), Float.valueOf(3));
在第一個電話中,我們有double
值,這些值會自動裝箱為Double.
然後,通過多態性, Double
精度匹配Number.
同樣, Float
與第二個呼叫的Number
讓我們觀察一個事實,即Float
和Double
都繼承自Number
和Object.
但是,默認綁定是Number
。這是由於Java將自動匹配與方法簽名匹配的最接近的超類型。
現在讓我們考慮以下方法調用:
obj.sum(2, "John");
在此示例中,我們為第一個參數設置了int
到Integer
但是,此方法名稱sum(Integer, String)
因此,Java將遍歷所有參數超類型,以從最接近的父代強制轉換為Object
直到找到匹配項為止。在這種情況下,它綁定到sum(Object, Object).
要更改默認綁定,我們可以使用顯式參數強制轉換,如下所示:
obj.sum((Object) 2, (Object) 3);
obj.sum((Number) 2, (Number) 3);
5. Vararg參數
現在,我們將注意力轉移到**varargs
如何影響方法的有效簽名**和靜態綁定。
這裡我們有一個使用varargs
的重載方法:
public Number sum(Object term1, Object term2) {
System.out.println("Adding objects");
return term1.hashCode() + term2.hashCode();
}
public Number sum(Object term1, Object... term2) {
System.out.println("Adding variable arguments: " + term2.length);
int result = term1.hashCode();
for (Object o : term2) {
result += o.hashCode();
}
return result;
}
那麼這些方法的有效簽名是什麼?我們已經看到sum(Object, Object)
是第一個的簽名。變量參數本質上是數組,因此編譯後第二個變量的有效簽名是sum(Object, Object[]).
一個棘手的問題是,只有兩個參數時,如何選擇方法綁定?
讓我們考慮以下調用:
obj.sum(new Object(), new Object());
obj.sum(new Object(), new Object(), new Object());
obj.sum(new Object(), new Object[]{new Object()});
顯然,第一個調用將綁定到sum(Object, Object)
,第二個sum(Object, Object[]).
要強制Java使用兩個對象來調用第二個方法,我們必須像在第三個調用中一樣將其包裝在數組中。
這裡要注意的最後一件事是,聲明以下方法將與vararg版本衝突:
public Number sum(Object term1, Object[] term2) {
// ...
}
六,結論
在本教程中,我們了解到方法簽名由名稱和參數類型的列表組成。修飾符,返回類型,參數名稱和異常列表無法區分重載方法,因此也不是簽名的一部分。
我們還研究了類型擦除和varargs如何隱藏有效的方法簽名,以及如何覆蓋Java的靜態方法綁定。