不定長度引數/內部類別
May 30, 2022在呼叫方法時,若方法的引數個數事先無法決定該如何處理?例如 System.out.printf
方法就無法事先決定引數個數:
System.out.printf("%d", 10);
System.out.printf("%d %d", 10, 20);
System.out.printf("%d %d %d", 10, 20, 30);
不定長度引數
不定長度引數(Variable-length Argument)可以輕鬆解決這個問題。直接來看示範:
package cc.openhome;
public class MathTool {
public static int sum(int... numbers) {
int sum = 0;
for(int number : numbers) {
sum += number;
}
return sum;
}
}
要使用不定長度引數,宣告參數列時要於型態關鍵字後加上 ...
,在 sum
方法可使用增強式 for
迴圈來取得不定長度引數中的每個元素,你可以如此使用:
System.out.println(MathTool.sum(1, 2));
System.out.println(MathTool.sum(1, 2, 3));
System.out.println(MathTool.sum(1, 2, 3, 4));
不定長度引數是編譯器蜜糖,反編譯後就可以一窺究竟:
public static transient int sum(int ai[]) {
int i = 0;
int ai1[] = ai;
int j = ai1.length;
for(int k = 0; k < j; k++) {
int l = ai1[k];
i += l;
}
return i;
}
不定長度引數實際是展開為陣列,而呼叫不定長度引數的客戶端,例如 System.out.println(MathTool.sum(1, 2, 3))
,展開後也是變為陣列:
System.out.println(
sum(new int[] {1, 2, 3})
);
使用不定長度引數時,方法上宣告的不定長度參數必須是參數列最後一個。例如以下是合法宣告:
public void some(int arg1, int arg2, int... varargs) {
...
}
以下方式是不合法宣告:
public void some(int... varargs, int arg1, int arg2) {
...
}
使用兩個以上不定長度引數也是不合法的:
public void some(int... varargs1, int... varargs2) {
...
}
內部類別
可以在類別中再定義類別,稱為內部類別(Inner class),例如以下程式片段建立了非靜態的內部類別:
class Some {
class Other {
}
}
雖然實務上很少看到接下來的寫法,不過要使用 Some
的 Other
類別,必須先建立實例 Some
實例。例如:
var s = new Some();
Some.Other o = s.new Other();
內部類別也可以使用 public
、private
等宣告。例如:
class Some {
private class Other {
}
}
內部類別本身可以存取外部類別的成員,通常非靜態內部類別會宣告為 private
,這類內部類別是輔助類別中某些操作而設計,外部不用知道內部類別的存在。
內部類別也可以宣告為 static
。例如:
class Some {
static class Other {
}
}
一個被宣告為 static
的內部類別,是將外部類別當作名稱空間。你可以如下建立類別實例:
Some.Other o = new Some.Other();
被宣告為 static
的內部類別,是將外部類別當作名稱空間,是個獨立類別,它可以存取外部類別 static
成員,但不可存取外部類別非 static
成員。
方法中也可以宣告類別,這通常是輔助方法中演算之用,方法外無法使用。例如:
class Some {
public void doSome() {
class Other {
}
}
}
實務上比較少看到在方法中定義具名的內部類別,倒很常看到方法中定義匿名內部類別(Anonymous inner class)並直接實例化,這跟類別繼承或介面實作有關,以下先看一下語法,細節留到談到繼承與介面時再作討論:
var o = new Object() {
public String toString() {
return "無聊的語法示範而已";
}
};
如果要稍微解釋一下,這個語法定義了一個沒有名稱的類別,它繼承 Object
類別,並重新定義(Override)了 toString
方法,new
表示實例化這個沒有名稱的類別。
匿名類別語法本身,在某些場合有時有些囉嗦,Lambda 有一部份目的是用來解決匿名類別語法囉嗦的問題,之後會再討論。