Điều này không biên dịch, bất kỳ đề xuất nào được đánh giá cao.
...
List<Object> list = getList();
return (List<Customer>) list;
Trình biên dịch nói: không thể truyền List<Object>
sangList<Customer>
Điều này không biên dịch, bất kỳ đề xuất nào được đánh giá cao.
...
List<Object> list = getList();
return (List<Customer>) list;
Trình biên dịch nói: không thể truyền List<Object>
sangList<Customer>
Câu trả lời:
bạn luôn có thể truyền bất kỳ đối tượng nào sang bất kỳ kiểu nào bằng cách truyền nó lên Đối tượng trước. trong trường hợp của bạn:
(List<Customer>)(Object)list;
bạn phải chắc chắn rằng trong thời gian chạy danh sách không chứa gì ngoài các đối tượng Khách hàng.
Các nhà phê bình nói rằng việc truyền như vậy chỉ ra điều gì đó không ổn với mã của bạn; bạn sẽ có thể điều chỉnh khai báo kiểu của mình để tránh nó. Nhưng Java generics quá phức tạp và nó không hoàn hảo. Đôi khi bạn chỉ không biết liệu có một giải pháp tốt nào đó để đáp ứng trình biên dịch hay không, mặc dù bạn biết rất rõ các kiểu thời gian chạy và bạn biết những gì bạn đang cố gắng làm là an toàn. Trong trường hợp đó chỉ cần đúc thô khi cần thiết là có thể nghỉ việc về nhà.
@SuppressWarnings("unchecked")
. Lưu ý rằng bạn cũng có thể upcast lên (List)
thay vì tới (Object)
.
Đó là bởi vì mặc dù Khách hàng là Đối tượng, nhưng Danh sách Khách hàng không phải là Danh sách Đối tượng. Nếu có, thì bạn có thể đưa bất kỳ đối tượng nào vào danh sách Khách hàng.
.Cast<T>()
và một được gọi .OfType<T>()
. Phần tử trước thực hiện ép kiểu trên từng phần tử (ném các ngoại lệ mong muốn) trong khi phần tử thứ hai lọc ra các phần tử không thể ép kiểu (vì vậy bạn sẽ chọn một phần tử tùy thuộc vào tình huống sử dụng của mình).
instanceof
Khách hàng?
Tùy thuộc vào mã khác của bạn, câu trả lời tốt nhất có thể khác nhau. Thử:
List<? extends Object> list = getList();
return (List<Customer>) list;
hoặc là
List list = getList();
return (List<Customer>) list;
Nhưng hãy nhớ rằng không nên thực hiện các phôi không được kiểm tra như vậy.
Với Java 8 Streams :
Đôi khi ép buộc vũ phu là tốt:
List<MyClass> mythings = (List<MyClass>) (Object) objects
Nhưng đây là một giải pháp linh hoạt hơn:
List<Object> objects = Arrays.asList("String1", "String2");
List<String> strings = objects.stream()
.map(element->(String) element)
.collect(Collectors.toList());
Có rất nhiều lợi ích, nhưng một trong số đó là bạn có thể bỏ qua danh sách của mình một cách thanh lịch hơn nếu bạn không thể chắc chắn nó chứa những gì:
objects.stream()
.filter(element->element instanceof String)
.map(element->(String)element)
.collect(Collectors.toList());
FluentIterable
làm việc cho tôi.
Bạn có thể sử dụng một diễn viên kép.
return (List<Customer>) (List) getList();
Lưu ý rằng tôi không phải là lập trình viên java, nhưng trong .NET và C #, tính năng này được gọi là tương phản hoặc hiệp phương sai. Tôi chưa đi sâu vào những điều đó, vì chúng là mới trong .NET 4.0, tôi không sử dụng vì nó chỉ là phiên bản beta, vì vậy tôi không biết thuật ngữ nào trong số hai thuật ngữ mô tả vấn đề của bạn, nhưng hãy để tôi mô tả vấn đề kỹ thuật với điều này.
Giả sử bạn đã được phép cast. Lưu ý, tôi nói là truyền , vì đó là những gì bạn đã nói, nhưng có hai thao tác có thể thực hiện được, truyền và chuyển đổi .
Chuyển đổi có nghĩa là bạn nhận được một đối tượng danh sách mới, nhưng bạn nói là truyền, có nghĩa là bạn muốn tạm thời coi một đối tượng này là một kiểu khác.
Đây là vấn đề với điều đó.
Điều gì sẽ xảy ra nếu điều sau được cho phép (lưu ý, tôi giả sử rằng trước khi ép kiểu, danh sách các đối tượng thực sự chỉ chứa các đối tượng Khách hàng, nếu không quá trình ép kiểu sẽ không hoạt động ngay cả trong phiên bản java giả định này):
List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];
Trong trường hợp này, điều này sẽ cố gắng xử lý một đối tượng, không phải là khách hàng, là khách hàng và bạn sẽ gặp lỗi thời gian chạy tại một thời điểm, hoặc biểu mẫu bên trong danh sách hoặc từ nhiệm vụ.
Tuy nhiên, Generics được cho là cung cấp cho bạn các kiểu dữ liệu an toàn về kiểu chữ, chẳng hạn như bộ sưu tập, và vì họ thích ném từ 'đảm bảo' vào xung quanh, loại diễn viên này, với các vấn đề sau đó, không được phép.
Trong .NET 4.0 (tôi biết, câu hỏi của bạn là về java), điều này sẽ được cho phép trong một số trường hợp rất cụ thể , nơi trình biên dịch có thể đảm bảo rằng các hoạt động bạn thực hiện là an toàn, nhưng theo nghĩa chung, kiểu truyền này sẽ không được cho phép. Điều tương tự cũng xảy ra với java, mặc dù tôi không chắc về bất kỳ kế hoạch nào để giới thiệu sự đồng biến và trái ngược với ngôn ngữ java.
Hy vọng rằng ai đó có kiến thức java tốt hơn tôi có thể cho bạn biết các chi tiết cụ thể cho việc triển khai hoặc tương lai java.
Một cách tiếp cận khác là sử dụng luồng java 8.
List<Customer> customer = myObjects.stream()
.filter(Customer.class::isInstance)
.map(Customer.class::cast)
.collect(toList());
List<Customer> cusList = new ArrayList<Customer>();
for(Object o: list){
cusList.add((Customer)o);
}
return cusList;
list.stream().forEach(x->cusList.add((Customer)x))
return cuslist;
Bạn không thể bởi vì List<Object>
vàList<Customer>
không ở trong cùng một cây thừa kế.
Bạn có thể thêm một phương thức khởi tạo mới vào List<Customer>
lớp của mình , lấy a List<Object>
và sau đó lặp qua danh sách truyền từng Object
hàm thành a Customer
và thêm nó vào bộ sưu tập của bạn. Hãy lưu ý rằng một ngoại lệ truyền không hợp lệ có thể xảy ra nếu người gọi List<Object>
chứa thứ gì đó không phải làCustomer
.
Điểm của danh sách chung là giới hạn chúng trong một số loại nhất định. Bạn đang cố gắng lấy một danh sách có thể có bất kỳ thứ gì trong đó (Đơn hàng, Sản phẩm, v.v.) và ép nó vào một danh sách chỉ có thể lấy Khách hàng.
Đặt cược tốt nhất của bạn là tạo mới List<Customer>
, lặp lại List<Object>
, thêm từng mục vào danh sách mới và trả lại.
Như những người khác đã chỉ ra, bạn không thể chọn chúng một cách tỉnh táo, vì a List<Object>
không phải là a List<Customer>
. Những gì bạn có thể làm là xác định một dạng xem trên danh sách để kiểm tra loại tại chỗ. Sử dụng Bộ sưu tập của Google sẽ là:
return Lists.transform(list, new Function<Object, Customer>() {
public Customer apply(Object from) {
if (from instanceof Customer) {
return (Customer)from;
}
return null; // or throw an exception, or do something else that makes sense.
}
});
Tương tự với Bozho ở trên. Bạn có thể thực hiện một số cách giải quyết khác ở đây (mặc dù bản thân tôi không thích nó) thông qua phương pháp này:
public <T> List<T> convert(List list, T t){
return list;
}
Đúng. Nó sẽ chuyển danh sách của bạn thành loại chung được yêu cầu của bạn.
Trong trường hợp đã cho ở trên, bạn có thể thực hiện một số mã như sau:
List<Object> list = getList();
return convert(list, new Customer());
Tùy thuộc vào những gì bạn muốn làm với danh sách, bạn thậm chí có thể không cần truyền nó đến a List<Customer>
. Nếu bạn chỉ muốn thêm Customer
các đối tượng vào danh sách, bạn có thể khai báo nó như sau:
...
List<Object> list = getList();
return (List<? super Customer>) list;
Điều này là hợp pháp (tốt, không chỉ hợp pháp mà còn đúng - danh sách là "một số loại siêu cấp cho Khách hàng") và nếu bạn định chuyển nó vào một phương thức chỉ đơn thuần là thêm các đối tượng vào danh sách thì ở trên giới hạn chung là đủ cho điều này.
Mặt khác, nếu bạn muốn truy xuất các đối tượng từ danh sách và yêu cầu chúng được đánh mạnh là Khách hàng - thì bạn đã không gặp may, và đúng như vậy. Bởi vì danh sách là mộtList<Object>
không có gì đảm bảo rằng nội dung là khách hàng, vì vậy bạn sẽ phải cung cấp tính năng truyền của riêng mình khi truy xuất. (Hoặc thực sự, hoàn toàn, chắc chắn rằng danh sách sẽ chỉ chứa Customers
và sử dụng kết hợp kép từ một trong các câu trả lời khác, nhưng hãy nhận ra rằng bạn đang hoàn toàn phá vỡ sự an toàn kiểu thời gian biên dịch mà bạn nhận được từ các generic trong phần này trường hợp).
Nói rộng ra, luôn tốt khi xem xét các giới hạn chung rộng nhất có thể có thể chấp nhận được khi viết một phương thức, gấp đôi nếu nó được sử dụng như một phương pháp thư viện. Ví dụ: nếu bạn chỉ đọc từ một danh sách, hãy sử dụng List<? extends T>
thay vì List<T>
- điều này cung cấp cho người gọi của bạn nhiều phạm vi hơn trong các đối số mà họ có thể chuyển vào và có nghĩa là họ ít có khả năng gặp phải các vấn đề có thể tránh được tương tự như bạn ' đang có ở đây.