Cara mentransmisikan List <Object> ke List <MyClass>

Jawaban:

156

Anda selalu dapat mentransmisikan objek apa pun ke jenis apa pun dengan mentransmisikannya ke Objek terlebih dahulu. dalam kasus Anda:

(List<Customer>)(Object)list; 

Anda harus yakin bahwa pada waktu proses daftar berisi apa-apa selain objek Pelanggan.

Kritikus mengatakan bahwa casting seperti itu menunjukkan ada yang salah dengan kode Anda; Anda harus dapat menyesuaikan deklarasi tipe Anda untuk menghindarinya. Tetapi obat generik Java terlalu rumit, dan itu tidak sempurna. Kadang-kadang Anda tidak tahu apakah ada solusi yang bagus untuk memuaskan kompiler, meskipun Anda tahu betul jenis runtime dan Anda tahu apa yang Anda coba lakukan aman. Dalam hal ini, lakukan pengecoran kasar sesuai kebutuhan, sehingga Anda dapat meninggalkan pekerjaan untuk rumah.

tak ternilai
sumber
8
Ini juga perlu @SuppressWarnings("unchecked"). Perhatikan bahwa Anda juga bisa upcast to (List)daripada to (Object).
200_berhasil
36

Itu karena meskipun Pelanggan adalah Objek, Daftar Pelanggan bukanlah Daftar Objek. Jika ya, maka Anda dapat memasukkan objek apa pun ke dalam daftar Pelanggan.

Brian Agnew
sumber
2
Tidak, tidak. Anda akan menghindari keamanan tipe jika hal di atas diizinkan. Perhatikan bahwa casting tidak berarti membuat daftar baru dan menyalin item. Ini berarti menangani satu instance sebagai jenis yang berbeda, dan dengan demikian Anda akan memiliki daftar yang berisi objek yang berpotensi bukan Pelanggan dengan jaminan keamanan jenis yang seharusnya tidak semestinya. Ini tidak ada hubungannya dengan java seperti itu. Karena Anda merasa fitur ini membuat Java terjebak dalam masa kegelapan, saya menantang Anda untuk menjelaskan bagaimana menurut Anda fitur ini seharusnya berfungsi.
Lasse V. Karlsen
1
@ BrainSlugs83 untuk kebutuhan Anda (Daftar <Customer>) (Objek) daftar;
Muthu Ganapathy Nathan
@ LasseV.Karlsen Saya tidak sedang berbicara tentang casting, saya berbicara tentang konversi. Itu tidak akan menghindari keamanan tipe, itu akan menegakkannya. Implementasi Java generik, kurangnya operator yang kelebihan beban, metode ekstensi, dan banyak fasilitas modern lainnya membuat saya sangat kecewa. - Sementara itu, .NET memiliki dua metode ekstensi terpisah untuk ini - satu dipanggil .Cast<T>()dan satu dipanggil .OfType<T>(). Yang pertama melakukan pemeran pada setiap elemen (melempar pengecualian yang diinginkan) sedangkan yang terakhir memfilter elemen yang tidak dapat dilemparkan (jadi Anda akan memilih satu tergantung pada skenario penggunaan Anda).
BrainSlugs83
1
@EAGER_STUDENT Saya tidak akan melupakan Java yang mungkin benar-benar berfungsi (semua bisnis penghapusan jenis konyol ini, saya harus mencobanya ...) - tetapi tidak, saya tidak akan pernah menulis kode seperti itu - Anda kalah jenis keamanan, apa yang terjadi jika salah satu elemen dalam koleksi bukan instanceofPelanggan?
BrainSlugs83
1
@ BrainSlugs83 Orang yang mengajukan pertanyaan secara khusus menanyakan tentang casting. Dan jika Java belum memiliki metode yang relevan seperti metode .NET yang Anda rujuk (yang masih belum mengonversi btw), maka Anda dapat dengan mudah menambahkannya. Ini semua agak ortogonal dengan pertanyaan yang ada, yang menanyakan tentang sintaks tertentu.
Lasse V. Karlsen
35

Bergantung pada kode Anda yang lain, jawaban terbaik mungkin berbeda. Mencoba:

List<? extends Object> list = getList();
return (List<Customer>) list;

atau

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

Namun perlu diingat bahwa tidak disarankan untuk melakukan cast yang tidak dicentang seperti itu.

Bozho
sumber
3
-1 - ini adalah kebiasaan yang sangat buruk, dan kecuali Anda menggunakan anotasi "Tidak dicentang", kompilator akan tetap mengeluh tentang hal itu
kdgregory
24

Dengan Java 8 Streams :

Terkadang casting brute force baik-baik saja:

List<MyClass> mythings = (List<MyClass>) (Object) objects

Tapi inilah solusi yang lebih serbaguna:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

Ada banyak sekali manfaatnya, tetapi salah satunya adalah Anda dapat memasukkan daftar Anda dengan lebih elegan jika Anda tidak yakin apa isinya:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());
bundar
sumber
1
Itu lebih merupakan salinan daripada pemeran.
200_berhasil
Pengecoran yang disebutkan dalam anser yang diterima tidak berhasil untuk saya. Saya juga berada di Java 7. Tapi Jambu biji FluentIterableberhasil untuk saya.
Sridhar Sarnobat
Ini yang saya cari, saya tidak tahu sintaksnya
Didorong oleh Kopi
23

Anda dapat menggunakan pemeran ganda.

return (List<Customer>) (List) getList();
Peter Lawrey
sumber
8

Perhatikan bahwa saya bukan programmer java, tetapi di .NET dan C #, fitur ini disebut kontravarian atau kovarian. Saya belum mempelajari hal-hal itu, karena mereka baru dalam .NET 4.0, yang tidak saya gunakan karena ini hanya beta, jadi saya tidak tahu istilah mana yang menggambarkan masalah Anda, tapi izinkan saya menjelaskan masalah teknis dengan ini.

Anggaplah Anda diizinkan untuk melakukan casting. Perhatikan, saya katakan cast , karena itu yang Anda katakan, tetapi ada dua operasi yang dapat dilakukan, casting dan konversi .

Mengonversi berarti Anda mendapatkan objek daftar baru, tetapi Anda mengatakan casting, yang berarti Anda ingin memperlakukan satu objek untuk sementara sebagai tipe lain.

Inilah masalahnya.

Apa yang akan terjadi jika yang berikut diizinkan (perhatikan, saya berasumsi bahwa sebelum cast, daftar objek sebenarnya hanya berisi objek Customer, jika tidak cast tidak akan berfungsi bahkan dalam versi java hipotetis ini):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

Dalam kasus ini, ini akan mencoba untuk memperlakukan objek, yang bukan pelanggan, sebagai pelanggan, dan Anda akan mendapatkan kesalahan waktu proses pada satu titik, baik formulir di dalam daftar, atau dari tugas.

Generik, bagaimanapun, seharusnya memberi Anda tipe data yang aman untuk tipe, seperti koleksi, dan karena mereka suka menggunakan kata 'dijamin', pemeran semacam ini, dengan masalah yang mengikutinya, tidak diperbolehkan.

Di .NET 4.0 (Saya tahu, pertanyaan Anda adalah tentang java), ini akan diizinkan dalam beberapa kasus yang sangat spesifik , di mana kompiler dapat menjamin bahwa operasi yang Anda lakukan aman, tetapi dalam pengertian umum, jenis pemeran ini tidak akan diperkenankan. Hal yang sama berlaku untuk java, meskipun saya tidak yakin tentang rencana apa pun untuk memperkenalkan co- dan contravariance ke bahasa java.

Mudah-mudahan, seseorang dengan pengetahuan java yang lebih baik dari saya dapat memberitahu Anda secara spesifik untuk masa depan atau implementasi java.

Lasse V. Karlsen
sumber
3
Masa depan Java adalah ... Scala. Benar-benar, orang yang menghubungkan obat generik ke Java telah mengembangkan bahasa baru yang agak mirip dengan Java tetapi sangat, sangat mahir dengan tipe: Penerapan penanganan tipe yang sangat teliti dan konsisten. Saya rasa tidak ada yang tahu pasti fitur Scala mana yang akan mengalir kembali ke Java, dan kapan.
Carl Smotricz
penjelasan yang sangat baik tentang kovarian yang benar-benar menjawab pertanyaan OP. Sudah selesai dilakukan dengan baik.
Kevin Day
Carl: Saya pikir beberapa pengembang Java terus membuat C #? :) Pokoknya ya, Java kemungkinan besar akan mengarah ke Scala di masa depan daripada, katakanlah, sesuatu yang kurang diketik dengan kuat.
Esko
@Carl - di Scala ada perbedaan kecil yang secara default Daftar tidak dapat diubah. Jadi secara umum yo tidak memiliki masalah menambahkan Objek untuk daftar pelanggan, karena ketika Anda melakukan ini Anda mendapatkan baru daftar Objects .
Brian Agnew
Er ... secara teknis ini benar - tetapi bahkan sebelum .NET 4.0 - Anda dapat melakukan ini dengan metode ekstensi IEnumerable biasa (.Cast <> dan .OfType <>) - jadi tidak perlu berlebihan jika Anda hanya ingin iterasi tipe yang kuat.
BrainSlugs83
7

Pendekatan lain akan menggunakan aliran java 8.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());
d0x
sumber
1
terima kasih, solusi ini sangat indah, menggunakan referensi metode
thang
1
Tidak pernah terpikir seseorang akan menggulir sejauh itu: D
d0x
3

Anda hanya harus mengulang daftar dan melemparkan semua Objek satu per satu

vrm
sumber
3

Anda bisa melakukan sesuatu seperti ini

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

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

return cusList; 

Atau cara Java 8

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

return cuslist;
Malkeith Singh
sumber
2

Anda tidak bisa karena List<Object>dan List<Customer>tidak berada dalam pohon warisan yang sama.

Anda dapat menambahkan konstruktor baru ke List<Customer>kelas Anda yang membutuhkan List<Object>dan kemudian mengulang melalui daftar yang mentransmisikan masing Object- masing ke a Customerdan menambahkannya ke koleksi Anda. Ketahuilah bahwa pengecualian cast yang tidak valid dapat terjadi jika pemanggil List<Object>berisi sesuatu yang bukan Customer.

Inti dari daftar umum adalah untuk membatasi mereka ke tipe tertentu. Anda mencoba mengambil daftar yang dapat memuat apa saja (Pesanan, Produk, dll.) Dan memasukkannya ke dalam daftar yang hanya dapat memuat Pelanggan.

Rob Sobers
sumber
2

Anda dapat membuat Daftar baru dan menambahkan elemen ke dalamnya:

Sebagai contoh:

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

Taruhan terbaik Anda adalah membuat yang baru List<Customer>, mengulangi List<Object>, menambahkan setiap item ke daftar baru, dan mengembalikannya.

Aric TenEyck
sumber
1

Seperti yang ditunjukkan orang lain, Anda tidak dapat membuangnya dengan hati-hati, karena a List<Object>bukanlah a List<Customer>. Apa yang dapat Anda lakukan, adalah menentukan tampilan pada daftar yang melakukan pemeriksaan tipe di tempat. Menggunakan Koleksi Google yang akan:

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

Mirip dengan Bozho di atas. Anda dapat melakukan beberapa solusi di sini (meskipun saya sendiri tidak menyukainya) melalui metode ini:

public <T> List<T> convert(List list, T t){
    return list;
}

Iya. Ini akan memasukkan daftar Anda ke dalam tipe generik yang Anda minta.

Dalam kasus yang diberikan di atas, Anda dapat melakukan beberapa kode seperti ini:

    List<Object> list = getList();
    return convert(list, new Customer());
Hendra Jaya
sumber
Saya suka solusi ini. Meskipun Anda perlu menambahkan SuppressWarnings, lebih baik menambahkannya di satu tempat daripada di setiap casting yang tidak aman.
robson
1

Bergantung pada apa yang ingin Anda lakukan dengan daftar tersebut, Anda mungkin tidak perlu mentransmisikannya ke file List<Customer>. Jika Anda hanya ingin menambahkan Customerobjek ke daftar, Anda dapat mendeklarasikannya sebagai berikut:

...
List<Object> list = getList();
return (List<? super Customer>) list;

Ini legal (yah, bukan hanya legal, tapi benar - daftarnya adalah "beberapa supertipe untuk Pelanggan"), dan jika Anda akan meneruskannya ke metode yang hanya akan menambahkan objek ke daftar maka di atas batas umum sudah cukup untuk ini.

Di sisi lain, jika Anda ingin mengambil objek dari daftar dan mengetiknya dengan kuat sebagai Pelanggan - maka Anda kurang beruntung, dan memang demikian. Karena daftarnya adalah List<Object>, tidak ada jaminan bahwa isinya adalah pelanggan, jadi Anda harus menyediakan casting Anda sendiri saat pengambilan. (Atau benar-benar, yakin sekali bahwa daftar hanya akan berisi Customersdan menggunakan pemeran ganda dari salah satu jawaban lain, tetapi sadari bahwa Anda sepenuhnya menghindari keamanan jenis waktu kompilasi yang Anda peroleh dari obat generik dalam hal ini. kasus).

Secara umum, selalu baik untuk mempertimbangkan batas generik seluas mungkin yang dapat diterima saat menulis metode, begitu juga jika itu akan digunakan sebagai metode pustaka. Jika Anda hanya akan membaca dari daftar, gunakan List<? extends T>bukan List<T>, misalnya - ini memberikan penelepon Anda jauh lebih lingkup dalam argumen mereka dapat lulus dalam dan sarana mereka cenderung mengalami masalah dihindari mirip dengan yang Anda' mengalami di sini.

Andrzej Doyle
sumber