Làm cách nào để sửa "Biểu thức của Danh sách loại cần chuyển đổi không được kiểm tra '?


137

Trong đoạn mã Java:

SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<SyndEntry> entries = sf.getEntries();

dòng cuối cùng tạo cảnh báo

"Biểu thức của loại Listcần chuyển đổi không được kiểm tra để phù hợp với List<SyndEntry>"

Cách thích hợp để khắc phục điều này là gì?

Câu trả lời:


96

Kể từ khi getEntriestrả về một nguyên List, nó có thể giữ bất cứ điều gì.

Cách tiếp cận không có cảnh báo là tạo một cái mới List<SyndEntry>, sau đó bỏ từng yếu tố của sf.getEntries()kết quả vào SyndEntrytrước khi thêm nó vào danh sách mới của bạn. Collections.checkedListkhông không làm kiểm tra này cho bạn-mặc dù nó đã có thể thực hiện nó để làm như vậy.

Bằng cách tự thực hiện trước, bạn "tuân thủ các điều khoản bảo hành" của các tổng quát Java: nếu ClassCastExceptionđược nâng lên, nó sẽ được liên kết với một chuỗi trong mã nguồn, không phải là một trình đúc vô hình được trình biên dịch chèn vào.


9
Cảm ơn - đó là một cái nhìn sâu sắc thú vị về "bảo hành" và diễn viên vô hình được thực hiện bởi trình biên dịch so với một diễn viên được thực hiện rõ ràng trong mã của riêng tôi.
dùng46277

1
Vâng, giá trị của thuốc generic chưa thống nhất có phần hạn chế, nhưng đó là một điều nó cung cấp. Chỉ cần làm rõ, điều này đòi hỏi mã của bạn biên dịch mà không có cảnh báo an toàn kiểu.
erickson

Xin chào erickson, tôi đồng ý rằng đây thực sự là giải pháp tốt nhất. Kiểm tra câu trả lời của tôi stackoverflow.com/questions/367626/ cho một phiên bản chung của giải pháp này.
Bruno De Fraine

115

Đây là một vấn đề phổ biến khi xử lý các API trước Java 5. Để tự động hóa giải pháp từ erickson , bạn có thể tạo phương thức chung sau:

public static <T> List<T> castList(Class<? extends T> clazz, Collection<?> c) {
    List<T> r = new ArrayList<T>(c.size());
    for(Object o: c)
      r.add(clazz.cast(o));
    return r;
}

Điều này cho phép bạn làm:

List<SyndEntry> entries = castList(SyndEntry.class, sf.getEntries());

Bởi vì giải pháp này kiểm tra rằng các phần tử thực sự có loại phần tử chính xác bằng phương pháp đúc, nên nó an toàn và không yêu cầu SuppressWarnings.


5
Về phương pháp được đề xuất bởi Bruno, điều này có làm ảnh hưởng đến hiệu suất ứng dụng khi có Danh sách có nhiều yếu tố không?. Java sẽ phải bỏ từng cái một.
will824

2
Nếu bạn muốn đảm bảo, đó là chi phí. Có một lựa chọn khác ít tốn kém hơn? Rõ ràng, nếu bạn có quyền kiểm soát phương thức trả về bộ sưu tập thô được gọi, hoặc thậm chí gọi phương thức hoặc truy cập bộ sưu tập bằng cách sử dụng phương pháp lười theo yêu cầu. Bất cứ điều gì xem xét toàn bộ bộ sưu tập sau khi gọi phương thức?
dan

28

Có vẻ như SyndFeedkhông sử dụng thuốc generic.

Bạn có thể có một dàn diễn viên không an toàn và ngăn chặn cảnh báo:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = (List<SyndEntry>) sf.getEntries();

hoặc gọi Bộ sưu tập.checkedList - mặc dù bạn vẫn cần phải loại bỏ cảnh báo:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = Collections.checkedList(sf.getEntries(), SyndEntry.class);

Vì cả hai đều ngăn chặn cảnh báo, bất kỳ lợi thế cho người này hay người kia, hoặc một sở thích? Cảm ơn! Ngoài ra: diễn viên có cần thiết không nếu sự đàn áp không được kiểm soát diễn ra?
Dan Rosenstark

3
@Yar: Vâng, Collections.checkedListsẽ ngăn chặn việc thêm bất kỳ yếu tố không phải SyndEntry nào sau này. Cá nhân tôi không sử dụng checkedListnhiều, nhưng sau đó tôi cũng không thường xuyên rơi vào tình huống diễn viên không được kiểm soát này ...
Jon Skeet

9

Bạn đã viết SyndFeed?

sf.getEntriestrả lại Danh sách hay List<SyndEntry>không? Tôi đoán là nó trở lại Listvà thay đổi nó trở lại List<SyndEntry>sẽ khắc phục vấn đề.

Nếu SyndFeedlà một phần của thư viện, tôi không nghĩ bạn có thể xóa cảnh báo mà không cần thêm @SuppressWarning("unchecked")chú thích vào phương thức của mình.


Bạn cũng có thể thêm một diễn viên rõ ràng.
Uri

3
Một diễn viên sẽ chỉ đưa ra một cảnh báo khác, vì mã không phải là loại an toàn.
erickson

SyndFeedđến từ rometools.github.io/rome/ROMERelease/ROME1.0Release.html . Vấn đề dường như đã được khắc phục trong các phiên bản gần đây hơn của Rome giống như các phiên bản được tìm thấy tại mvnreposective.com/artifact/com.rometools/rome/1.9.0
daloonik

2

Nếu bạn đang sử dụng Guava và tất cả những gì bạn muốn làm là lặp qua các giá trị của bạn:

for(SyndEntry entry: Iterables.filter(sf.getEntries(), SyndEntry.class){
  ...
}

Nếu bạn cần một Danh sách thực tế, bạn có thể sử dụng

List<SyndEntry> list = Lists.newArrayList(
    Iterables.filter(sf.getEntries(), SyndEntry.class));

hoặc là

List<SyndEntry> list = ImmutableList.copyOf(
    Iterables.filter(sf.getEntries(), SyndEntry.class));

1
SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<?> entries = sf.getEntries();

2
Ngay cả khi mã được cung cấp ở đây giải quyết vấn đề, tôi vẫn khuyến khích bạn giải thích ngắn gọn lý do tại sao nó làm điều đó. Hãy giải thích tại sao câu trả lời được đăng giải quyết vấn đề.
sbrattla

1

Nếu bạn xem javadoc cho lớp SyndFeed(tôi đoán bạn đang tham khảo lớp com.sun.syndication.feed.synd.SyndFeed), phương thức getEntries () không trả về java.util.List<SyndEntry>, nhưng chỉ trả về java.util.List.

Vì vậy, bạn cần một diễn viên rõ ràng cho việc này.


0

Nếu bạn không muốn đặt @SuppressWarning ("không được chọn") cho mỗi cuộc gọi sf.getEntries (), bạn luôn có thể tạo một trình bao bọc sẽ trả về Danh sách.

Xem câu hỏi khác


0

Thậm chí dễ dàng hơn

return new ArrayList<?>(getResultOfHibernateCallback(...))


Sau đó, bạn sẽ xử lý việc truyền đúng (truyền lại?) Tại thời điểm sử dụng cho từng thành phần trong ArrayList <?>.
ingyhere
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.