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) ความแตกต่างที่ฉันกล่าวถึงข้างต้นซับซ้อนเกินไปที่จะแยกแยะได้ง่ายโดยอัตโนมัติ