1) มีตัวอย่างมากมายบนอินเทอร์เน็ตและใน StackOverflow เกี่ยวกับปัญหาเฉพาะกับ generics และ varargs โดยพื้นฐานแล้วเมื่อคุณมีอาร์กิวเมนต์จำนวนตัวแปรประเภทพารามิเตอร์:
<T> void foo(T... args);
ใน Java, varargs เป็นน้ำตาล syntactic ที่ผ่านการ "เขียนใหม่" อย่างง่ายที่รวบรวมเวลา: พารามิเตอร์ varargs ประเภทX...ถูกแปลงเป็นพารามิเตอร์ประเภทX[]; และเวลาที่สายทำนี้ varargs ทุกวิธีคอมไพเลอร์จะเก็บรวบรวมทั้งหมดของ "ข้อโต้แย้งตัวแปร" ที่จะไปใน varargs new X[] { ...(arguments go here)... }พารามิเตอร์และสร้างอาร์เรย์เช่นเดียวกับ
นี้ทำงานได้ดีเมื่อ varargs String...ประเภทเป็นรูปธรรมเช่น เมื่อมันเป็นตัวแปรประเภทT...มันก็ยังทำงานเมื่อTเป็นที่รู้จักกันเป็นประเภทที่เป็นรูปธรรมสำหรับการโทรนั้น เช่นถ้าวิธีการข้างต้นเป็นส่วนหนึ่งของชั้นเรียนFoo<T>และคุณมีการFoo<String>อ้างอิงแล้วการเรียกfooมันจะไม่เป็นไรเพราะเรารู้ว่าTอยู่Stringที่จุดนั้นในรหัส
อย่างไรก็ตามมันจะไม่ทำงานเมื่อ "value" ของTเป็นพารามิเตอร์ประเภทอื่น ใน Java มันเป็นไปไม่ได้ที่จะสร้างอาร์เรย์ของประเภทองค์ประกอบพารามิเตอร์ ( new T[] { ... }) ดังนั้น Java ใช้แทนnew Object[] { ... }(นี่ObjectคือขอบเขตสูงสุดของT; หากมีขอบเขตบนมีบางอย่างที่แตกต่างกันก็จะเป็นที่แทนที่จะObject) และจากนั้นให้คำเตือนคอมไพเลอร์
ดังนั้นจะเกิดอะไรขึ้นกับการสร้างnew Object[]แทนที่จะเป็นnew T[]หรืออะไรก็ตาม ดีอาร์เรย์ใน Java รู้ว่าประเภทองค์ประกอบของพวกเขาที่รันไทม์ ดังนั้นวัตถุอาร์เรย์ที่ส่งผ่านจะมีประเภทองค์ประกอบที่ไม่ถูกต้องในขณะทำงาน
สำหรับการใช้งาน varargs ที่พบได้บ่อยที่สุดเพียงแค่วนซ้ำองค์ประกอบต่างๆนี่ก็ไม่มีปัญหา (คุณไม่สนใจเกี่ยวกับประเภทรันไทม์ของอาร์เรย์) ดังนั้นสิ่งนี้จึงปลอดภัย:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
อย่างไรก็ตามสำหรับทุกสิ่งที่ขึ้นอยู่กับประเภทองค์ประกอบรันไทม์ของอาร์เรย์ที่ส่งผ่านจะไม่ปลอดภัย นี่คือตัวอย่างง่ายๆของสิ่งที่ไม่ปลอดภัยและล้มเหลว:
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
นี่คือปัญหาที่เราขึ้นอยู่กับชนิดของการargsที่จะเป็นเพื่อที่จะกลับเป็นT[] แต่จริงๆแล้วประเภทของการโต้แย้งที่รันไทม์ที่ไม่ได้เป็นตัวอย่างของT[]T[]
3) หากวิธีการของคุณมีอาร์กิวเมนต์เป็นประเภทT...(โดยที่ T เป็นพารามิเตอร์ประเภทใด ๆ ) ดังนั้น:
- ปลอดภัย: หากวิธีการของคุณขึ้นอยู่กับความจริงที่ว่าองค์ประกอบของอาร์เรย์เป็นอินสแตนซ์ของ
T
- ไม่ปลอดภัย: ถ้ามันขึ้นอยู่กับความจริงที่ว่าอาร์เรย์เป็นตัวอย่างของ
T[]
สิ่งที่ขึ้นอยู่กับชนิดรันไทม์ของอาเรย์รวมถึง: คืนมันเป็นประเภทT[]ผ่านมันเป็นข้อโต้แย้งไปยังพารามิเตอร์ประเภทT[]รับประเภทอาร์เรย์โดยใช้.getClass()ผ่านไปยังวิธีการที่ขึ้นอยู่กับประเภทรันไทม์ของอาเรย์เช่นList.toArray()และArrays.copyOf()ฯลฯ
2) ความแตกต่างที่ฉันกล่าวถึงข้างต้นซับซ้อนเกินไปที่จะแยกแยะได้ง่ายโดยอัตโนมัติ