1) Có nhiều ví dụ trên Internet và trên StackOverflow về vấn đề cụ thể với thuốc generic và varargs. Về cơ bản, đó là khi bạn có số lượng đối số của loại tham số loại:
<T> void foo(T... args);
Trong Java, varargs là một đường cú pháp trải qua một "cách viết lại" đơn giản tại thời gian biên dịch: một tham số varargs của loại X...
được chuyển đổi thành một tham số kiểu X[]
; và mỗi khi có một cuộc gọi được thực hiện cho phương thức varargs này, trình biên dịch sẽ thu thập tất cả các "đối số biến" có trong tham số varargs và tạo ra một mảng giống như new X[] { ...(arguments go here)... }
.
Điều này hoạt động tốt khi các loại varargs là cụ thể String...
. Khi đó là một biến kiểu như thế T...
, nó cũng hoạt động khi T
được biết là một kiểu cụ thể cho cuộc gọi đó. ví dụ như nếu phương pháp trên là một phần của một lớp Foo<T>
, và bạn có một Foo<String>
tài liệu tham khảo, sau đó gọi foo
vào nó sẽ ổn bởi vì chúng ta biết T
là String
tại thời điểm đó trong các mã.
Tuy nhiên, nó không hoạt động khi "giá trị" của T
là một tham số loại khác. Trong Java, không thể tạo một mảng kiểu thành phần tham số kiểu ( new T[] { ... }
). Vì vậy, Java thay vì sử dụng new Object[] { ... }
(ở đây Object
là giới hạn trên của T
; nếu có giới hạn trên là một cái gì đó khác, thì nó sẽ thay thế Object
), và sau đó đưa ra cảnh báo cho trình biên dịch.
Vì vậy, có gì sai với việc tạo ra new Object[]
thay vì new T[]
hoặc bất cứ điều gì? Chà, mảng trong Java biết loại thành phần của chúng khi chạy. Do đó, đối tượng mảng đã truyền sẽ có kiểu thành phần sai khi chạy.
Đối với việc sử dụng varargs phổ biến nhất, chỉ đơn giản là lặp lại các phần tử, điều này không có vấn đề gì (bạn không quan tâm đến kiểu thời gian chạy của mảng), vì vậy điều này là an toàn:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
Tuy nhiên, đối với bất cứ điều gì phụ thuộc vào loại thành phần thời gian chạy của mảng đã truyền, nó sẽ không an toàn. Đây là một ví dụ đơn giản về một cái gì đó không an toàn và gặp sự cố:
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");
}
}
Vấn đề ở đây là chúng tôi phụ thuộc vào loại args
sẽ được T[]
trả lại T[]
. Nhưng thực tế kiểu đối số trong thời gian chạy không phải là một thể hiện của T[]
.
3) Nếu phương thức của bạn có một đối số kiểu T...
(trong đó T là bất kỳ tham số loại nào), thì:
- An toàn: Nếu phương thức của bạn chỉ phụ thuộc vào thực tế là các phần tử của mảng là các thể hiện của
T
- Không an toàn: Nếu nó phụ thuộc vào thực tế là mảng là một thể hiện của
T[]
Những thứ phụ thuộc vào kiểu thời gian chạy của mảng bao gồm: trả về dưới dạng kiểu T[]
, chuyển nó làm đối số cho tham số kiểu T[]
, lấy kiểu mảng bằng cách sử dụng .getClass()
, chuyển nó sang các phương thức phụ thuộc vào kiểu thời gian chạy của mảng, như List.toArray()
và Arrays.copyOf()
, Vân vân.
2) Sự khác biệt tôi đã đề cập ở trên là quá phức tạp để có thể dễ dàng phân biệt tự động.