java: (String []) List.toArray () cung cấp cho ClassCastException


107

Đoạn mã sau (chạy trong android) luôn cung cấp cho tôi một ClassCastException ở dòng thứ 3:

final String[] v1 = i18nCategory.translation.get(id);
final ArrayList<String> v2 = new ArrayList<String>(Arrays.asList(v1));
String[] v3 = (String[])v2.toArray();

Nó cũng xảy ra khi v2 là Đối tượng [0] và cũng khi có các Chuỗi trong đó. Bất kỳ ý tưởng tại sao?


Bạn có thể muốn đọc về Covariance and Contravariance - en.wikipedia.org/wiki/…
Hut8

Còn về trường hợp T là một giao diện với phương thức gốc để khởi tạo.
sebaj

2
@LaceCard - điều này chỉ liên quan rất gián tiếp đến hiệp phương sai / tương phản. Vấn đề thực sự là đây là hệ quả trực tiếp của hành vi cụ thể của toArray()phương thức.
Stephen C

Câu trả lời:


249

Điều này là do khi bạn sử dụng

 toArray() 

nó trả về một Đối tượng [], đối tượng này không thể được truyền vào Chuỗi [] (ngay cả khi nội dung là Chuỗi) Điều này là do phương thức toArray chỉ nhận được một

List 

và không

List<String>

vì generics là một thứ duy nhất của mã nguồn và không có sẵn trong thời gian chạy và vì vậy nó không thể xác định loại mảng nào để tạo.

sử dụng

toArray(new String[v2.size()]);

phân bổ đúng loại mảng (Chuỗi [] và có kích thước phù hợp)


3
cuz Generics, đó là lý do tại sao
Kaleb Brasee

2
@Kaleb - không đúng. Hoặc ít nhất đó không phải là lý do ban đầu. Các toArrayphương thức tồn tại trước khi các lớp tập hợp là chung chung.
Stephen C

10
Đây là giải pháp chính xác, nhưng không phải là giải thích chính xác. Bạn không thể truyền Đối tượng [] thành Double [] vì đó là một tính năng ngôn ngữ, không có gì khác. Nó không liên quan đến Generics. Bạn có thể truyền Object thành Double giả sử rằng nó thực sự là Double. Vì vậy, về mặt logic, bạn có thể làm tương tự với một mảng, nhưng nó đơn giản không phải là một phần của ngôn ngữ. Nếu bạn truyền Object thành Double sẽ có một kiểm tra thời gian chạy để đảm bảo Object thực sự là Double. Nếu bạn truyền Double to Object thì không có kiểm tra thời gian chạy, vì Object nằm trong hệ thống phân cấp kế thừa của Double.
KyleM

5
Khi thực hiện việc này trong IntelliJ, tôi nhận được cảnh báo nên sử dụng toArray(new String[0])thay thế: "Trong các phiên bản Java cũ hơn sử dụng mảng có kích thước trước được khuyến nghị vì lệnh gọi phản chiếu cần thiết để tạo một mảng có kích thước thích hợp khá chậm. Tuy nhiên, do các bản cập nhật muộn của OpenJDK 6 lệnh gọi này được tạo ra bản chất, làm cho hiệu suất của phiên bản mảng trống giống nhau và đôi khi thậm chí còn tốt hơn. Ngoài ra, việc chuyển mảng có kích thước trước cũng nguy hiểm cho một tập hợp đồng thời vì có thể có một cuộc chạy đua giữa lệnh gọi sizetoArray. "
Mikepote

35

Bạn đang sử dụng sai toArray()

Hãy nhớ rằng các generic của Java chủ yếu là đường cú pháp. Một ArrayList không thực sự biết rằng tất cả các phần tử của nó là Chuỗi.

Để khắc phục sự cố của bạn, hãy gọi toArray(T[]). Trong trường hợp của bạn,

String[] v3 = v2.toArray(new String[v2.size()]);

Lưu ý rằng biểu mẫu được tổng quát hóa toArray(T[])trả về T[], vì vậy kết quả không cần phải được ép kiểu rõ ràng.


1
String[] v3 = v2.toArray(new String[0]); 

cũng thực hiện thủ thuật, lưu ý rằng bạn thậm chí không cần ép kiểu nữa khi ArrayType phù hợp được cấp cho phương thức.


0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Sử dụng như thế này.


1
Xin vui lòng! Định dạng mã của bạn! Chọn tất cả bằng cách nhấn "ctrl + k" hoặc thêm "` "trước chữ cái đầu tiên của mã và cuối cùng là chữ cái khác. Bạn cũng có thể chọn "{}" trong phần trợ giúp ở trên cùng khi viết câu trả lời!
MK
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.