Cách truyền Danh sách <Đối tượng> sang Danh sách <MyClass>


99

Đ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:


156

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à.


8
Điều này cũng cần @SuppressWarnings("unchecked"). Lưu ý rằng bạn cũng có thể upcast lên (List)thay vì tới (Object).
200_success

36

Đó là bởi vì mặc dù Khách hàng Đố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.


2
Không, không phải vậy. Bạn sẽ phá vỡ sự an toàn của loại nếu điều trên được cho phép. Lưu ý rằng truyền không có nghĩa là tạo danh sách mới và sao chép các mục. Nó có nghĩa là xử lý cá thể đơn lẻ như một kiểu khác, và do đó bạn sẽ có một danh sách chứa các đối tượng có khả năng không phải là Khách hàng với bảo đảm an toàn về kiểu mà nó không nên. Điều này không liên quan gì đến java. Vì bạn cảm thấy rằng tính năng này khiến Java bị mắc kẹt trong thời kỳ đen tối, tôi thách bạn giải thích cách bạn nghĩ rằng tính năng này sẽ hoạt động thay thế.
Lasse V. Karlsen

1
@ BrainSlugs83 cho danh sách (Danh sách <Khách hàng>) (Đối tượng) cần của bạn;
Muthu Ganapathy Nathan

@ LasseV.Karlsen Tôi không nói về truyền, tôi đang nói về chuyển đổi. Nó sẽ không phá vỡ sự an toàn của kiểu, nó sẽ thực thi nó. Việc triển khai các tính năng chung của Java, thiếu sự quá tải của toán tử, các phương thức mở rộng và nhiều tiện nghi hiện đại khác đã khiến tôi thất vọng tột độ. - Trong khi đó, .NET có hai phương thức mở rộng riêng biệt cho việc này - một được gọi .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).
BrainSlugs83

1
@EAGER_STUDENT Tôi sẽ không đặt nó qua Java mà có thể thực sự hoạt động (tất cả những công việc tẩy xóa vô lý này, tôi phải thử nó ...) - nhưng không, tôi sẽ không bao giờ viết mã như vậy - bạn sẽ mất loại an toàn, điều gì sẽ xảy ra nếu một phần tử trong bộ sưu tập không phải là instanceofKhách hàng?
BrainSlugs83

1
@ BrainSlugs83 Người đặt câu hỏi đã hỏi cụ thể về việc tuyển chọn. Và nếu Java chưa có các phương thức liên quan như các phương thức .NET mà bạn tham khảo (vẫn không chuyển đổi btw), thì bạn có thể dễ dàng thêm chúng. Tuy nhiên, tất cả điều này gần như trực giao với câu hỏi hiện tại, hỏi về cú pháp cụ thể.
Lasse V. Karlsen,

35

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.


3
-1 - đây là một thói quen thực sự xấu để có được vào, và trừ khi bạn sử dụng "không được kiểm soát" chú thích, trình biên dịch vẫn sẽ phàn nàn về nó
kdgregory

24

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());

1
Tuy nhiên, đó giống như một bản sao hơn là một diễn viên.
200_success

Việc đúc được đề cập trong anser được chấp nhận không phù hợp với tôi. Ngoài ra, tôi đã sử dụng Java 7. Nhưng Guava đã FluentIterablelàm việc cho tôi.
Sridhar Sarnobat

Đây là những gì tôi đang tìm kiếm, tôi chỉ không biết cú pháp
thúc đẩy bởi Cà phê

23

Bạn có thể sử dụng một diễn viên kép.

return (List<Customer>) (List) getList();

8

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ềnchuyể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.


3
Tương lai của Java là ... Scala. Nghiêm túc mà nói, người đàn ông đã phát triển generics vào Java đã phát triển một ngôn ngữ mới gần giống với Java nhưng thực sự, thực sự thành thạo với các kiểu: Việc triển khai xử lý kiểu rất triệt để và nhất quán. Tôi nghĩ không ai biết chắc chắn những tính năng Scala nào sẽ quay trở lại Java và khi nào.
Carl Smotricz

một lời giải thích tuyệt vời về hiệp phương sai thực sự trả lời câu hỏi OPs. Làm tốt.
Ngày của Kevin

Carl: Tôi nghĩ rằng một số nhà phát triển Java đã đi trước để tạo C #? :) Dù sao thì, Java rất có thể sẽ đi theo hướng Scala trong tương lai thay vì một thứ gì đó ít được đánh máy mạnh hơn.
Esko

@Carl - trong Scala có một sự khác biệt nhỏ ở chỗ, theo mặc định, Danh sách là bất biến. Vì vậy, nói chung là yo không có vấn đề của việc thêm một đối tượng vào một danh sách các khách hàng, kể từ khi bạn làm điều này bạn sẽ có được một mới danh sách các đối tượng .
Brian Agnew

Ơ ... về mặt kỹ thuật thì điều này đúng - nhưng ngay cả trước .NET 4.0 - bạn có thể làm điều này với các phương thức mở rộng IEnumerable thông thường (.Cast <> và .OfType <>) - vì vậy không cần phải đi sâu nếu bạn chỉ muốn lặp lại kiểu mạnh.
BrainSlugs83

7

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());

1
cảm ơn bạn, giải pháp này rất đẹp, sử dụng phương pháp tham khảo
thang

1
Chưa bao giờ nghĩ rằng ai đó sẽ cuộn xuống xa như vậy: D
d0x

3

Bạn chỉ nên lặp lại danh sách và truyền từng Đối tượng một


3

Bạn có thể làm điều gì đó như thế này

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

Hoặc cách Java 8

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

2

Bạn không thể bởi vì List<Object>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 Objecthàm thành a Customervà 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.


2

Bạn có thể tạo một Danh sách mới và thêm các phần tử vào đó:

Ví dụ:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

Đặ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.


1

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.
  }
});

1

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ôi thích giải pháp này. Ngay cả khi bạn cần thêm SuppressWarnings, tốt hơn hết là bạn nên thêm nó vào một nơi hơn là trong mỗi lần truyền không an toàn.
robson

1

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 Customercá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 Customersvà 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.

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.