Liste <Nesne> Listesini <MyClass> Listesine dönüştürme


Yanıtlar:


157

her zaman herhangi bir nesneyi önce Object'e yukarı çevirerek herhangi bir türe çevirebilirsiniz. Senin durumunda:

(List<Customer>)(Object)list; 

çalışma zamanında listenin Müşteri nesnelerinden başka hiçbir şey içermediğinden emin olmalısınız.

Eleştirmenler, bu tür bir atamanın kodunuzla ilgili yanlış bir şeyi gösterdiğini söylüyor; Bundan kaçınmak için tür bildirimlerinizi değiştirebilmelisiniz. Ancak Java jenerikleri çok karmaşık ve mükemmel değil. Bazen, çalışma zamanı türlerini çok iyi bilseniz ve ne yapmaya çalıştığınızın güvenli olduğunu bilseniz bile, derleyiciyi tatmin edecek güzel bir çözüm olup olmadığını bilemezsiniz. Bu durumda, gerektiği gibi ham dökümü yapın, böylece ev için işten ayrılabilirsiniz.


8
Bunun da ihtiyacı var @SuppressWarnings("unchecked"). (List)Bunun yerine, yukarı doğru da yayınlayabileceğinizi unutmayın (Object).
200_success

36

Bunun nedeni, Müşteri bir Nesne olmasına rağmen, Müşteri Listesi'nin Nesne Listesi olmamasıdır . Öyleyse, herhangi bir nesneyi Müşteri listesine koyabilirsiniz .


2
Hayır, değil. Yukarıdakilere izin verilseydi, tür güvenliğini atlatırdın. Yayınlamanın, yeni bir liste oluşturmak ve öğeleri kopyalamak anlamına gelmediğini unutmayın. Tek örneği farklı bir tür olarak ele almak anlamına gelir ve bu nedenle, olmaması gereken bir tür güvenlik garantisine sahip, potansiyel olarak Müşteri olmayan nesneleri içeren bir listeniz olur. Bunun java ile hiçbir ilgisi yok. Bu özelliğin Java'yı karanlık çağlarda bıraktığını düşündüğünüz için, bunun yerine nasıl çalışması gerektiğini düşündüğünüzü açıklamanız için size meydan okuyorum.
Lasse V.Karlsen

1
@ İhtiyaçlarınız için BrainSlugs83 (Liste <Müşteri>) (Nesne) listesi;
Muthu Ganapathy Nathan

@ LasseV.Karlsen Oyuncu seçimi hakkında konuşmuyorum, dönüştürmekten bahsediyorum. Tip güvenliğini engellemez, onu uygular. Java'nın jenerik uygulaması, operatör aşırı yüklerinin olmaması, genişletme yöntemleri ve diğer birçok modern olanak beni epey hayal kırıklığına uğrattı. - Bu arada, .NET'in bunun için iki ayrı genişletme yöntemi vardır - biri çağrılır .Cast<T>(), diğeri çağrılır .OfType<T>(). İlki, her bir öğe üzerinde bir cast gerçekleştirirken (istenen istisnaları atarak) ikincisi, dönüştürülemeyen öğeleri filtreler (böylece kullanım senaryonuza bağlı olarak birini seçersiniz).
BrainSlugs83

1
@EAGER_STUDENT Gerçekten işe yarayabilecek Java'yı geçmezdim (tüm bu saçma tür silme işi, denemek zorundayım ...) - ama hayır, asla böyle kod yazmam - kaybedersiniz güvenlik yazın, koleksiyondaki bir öğe instanceofMüşteri değilse ne olur ?
BrainSlugs83

1
@ BrainSlugs83 Soruyu soran kişi özellikle oyuncu seçimi hakkında soru sordu. Java, başvurduğunuz .NET yöntemleri gibi ilgili yöntemlere zaten sahip değilse (ki bu hala btw'yi dönüştürmüyor), o zaman bunları kolayca ekleyebilmeniz gerekir. Bu, belirli bir sözdizimi soran eldeki soruya tamamen diktir.
Lasse V. Karlsen

35

Diğer kodunuza bağlı olarak, en iyi cevap değişebilir. Deneyin:

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

veya

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

Ancak, bu tür kontrolsüz yayınların yapılmasının tavsiye edilmediğini unutmayın.


3
-1 - bu, içine girmek için gerçekten kötü bir alışkanlık ve "Kontrol edilmemiş" ek açıklamasını kullanmadığınız sürece, derleyici yine de şikayet edecek
kdgregory

25

Java 8 Akışı ile :

Bazen kaba kuvvet dökümü iyidir:

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

Ama işte daha çok yönlü bir çözüm:

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

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

Bir sürü faydası var, ancak bir tanesi, ne içerdiğinden emin olamazsanız, listenizi daha zarif bir şekilde yayınlayabilmenizdir:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

1
Yine de bu bir oyuncu kadrosundan çok bir kopya.
200_success

Kabul edilen cevapta belirtilen döküm benim için işe yaramadı. Ayrıca Java 7 kullanıyordum. Ama Guava FluentIterablebenim için çalıştı.
Sridhar Sarnobat

Bu aradığım şeydi, sözdizimini bilmiyordum
Fueled By Coffee

23

Çift alçı kullanabilirsiniz.

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

8

Java programcısı olmadığımı unutmayın, ancak .NET ve C # 'da bu özelliğe kontravaryans veya kovaryans denir. Henüz bunlara girmedim, çünkü .NET 4.0'da yeni olduklarından, sadece beta olduğu için kullanmıyorum, bu yüzden iki terimden hangisinin probleminizi tanımladığını bilmiyorum, ancak açıklamama izin verin bununla ilgili teknik sorun.

Rol almanıza izin verildiğini varsayalım. Not, döküm diyorum , çünkü söylediğiniz buydu, ancak mümkün olabilecek iki işlem vardır: döküm ve dönüştürme .

Dönüştürme, yeni bir liste nesnesi alacağınız anlamına gelir, ancak çevrim diyorsunuz, bu da bir nesneyi geçici olarak başka bir tür olarak ele almak istediğiniz anlamına gelir.

İşte bununla ilgili sorun.

Aşağıdakilere izin verilseydi ne olurdu (not, dökümden önce nesnelerin listesinin aslında yalnızca Müşteri nesnelerini içerdiğini varsayıyorum, aksi takdirde, java'nın bu varsayımsal sürümünde bile cast çalışmaz):

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

Bu durumda, bu, müşteri olmayan bir nesneyi müşteri olarak ele almaya çalışır ve bir noktada, listenin içinde veya atamadan bir çalışma zamanı hatası alırsınız.

Bununla birlikte, jeneriklerin size koleksiyonlar gibi güvenli veri türleri vermesi beklenir ve 'garantili' kelimesini etrafa saçmayı sevdikleri için, aşağıdaki sorunlarda bu tür dökümlere izin verilmez.

.NET 4.0'da (biliyorum, sorunuz java hakkındaydı), buna bazı çok özel durumlarda izin verilecektir . derleyicinin yaptığınız işlemlerin güvenli olduğunu garanti edebildiği , ancak genel anlamda bu tür yayınlama izin. Aynı şey java için de geçerli, ancak java diline ortak ve kontravaryans getirme planlarından emin değilim.

Umarım, benden daha iyi java bilgisine sahip biri size java geleceği veya uygulamasıyla ilgili ayrıntıları söyleyebilir.


3
Java'nın geleceği ... Scala. Cidden, jenerikleri Java'ya ekleyen adam, Java'ya belirsiz bir şekilde benzeyen ancak türler konusunda gerçekten çok yetkin olan yeni bir dil geliştirdi: Çok kapsamlı ve tutarlı bir tür işleme uygulaması. Bence kimse hangi Scala özelliklerinin Java'ya ne zaman döneceğini kesin olarak bilmiyor.
Carl Smotricz

OP'lerin sorusuna gerçekten cevap veren mükemmel bir kovaryans açıklaması. Aferin.
Kevin Day

Carl: Bazı Java geliştiricilerin C # oluşturmaya devam ettiğini sanıyordum? :) Her neyse, evet, Java muhtemelen daha az güçlü yazılmış bir şey yerine gelecekte Scala'nın yönüne gidecek.
Esko

@Carl - Scala'da, varsayılan olarak Listelerin değişmez olması arasında ince bir fark vardır. Genelde bir Müşteri listesine bir Nesne ekleme problemi yaşamazsınız, çünkü bunu yaptığınızda yeni bir Nesne listesi alırsınız .
Brian Agnew

Er ... teknik olarak bu doğrudur - ancak .NET 4.0'dan önce bile - bunu normal IEnumerable uzantı yöntemleriyle (.Cast <> ve .OfType <>) yapabilirsiniz - bu nedenle derin uçtan çıkmanıza gerek yoktur. sadece güçlü tip yinelemesi istiyorsunuz.
BrainSlugs83

7

Başka bir yaklaşım, bir java 8 akışı kullanmak olacaktır.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

1
teşekkür ederim, bu çözüm çok güzel, yöntem referansını kullanarak
thang

1
Birinin bu kadar aşağı
kaydıracağını

3

Sadece listeyi yinelemeli ve tüm Nesneleri tek tek atmalısınız.


3

Bunun gibi bir şey yapabilirsin

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

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

return cusList; 

Veya Java 8 yolu

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

return cuslist;

2

Yapamazsın çünkü List<Object>veList<Customer> aynı miras ağacında değilsiniz.

List<Customer>Sınıfınıza, bir alan alan yeni bir kurucu ekleyebilir List<Object>ve ardından her Objectbirini a'ya aktaran Customerve koleksiyonunuza ekleyen listeyi yineleyebilirsiniz . Arayan kişi List<Object>başka bir şey içermiyorsa geçersiz bir atama istisnasının meydana gelebileceğini unutmayın.Customer .

Genel listelerin amacı, onları belirli türlerle sınırlandırmaktır. İçinde herhangi bir şey bulunabilecek bir listeyi (Siparişler, Ürünler vb.) Alıp yalnızca Müşteri alabilecek bir listeye sıkıştırmaya çalışıyorsunuz.


2

Yeni bir Liste oluşturabilir ve ona öğeleri ekleyebilirsiniz:

Örneğin:

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

1

En iyi bahsiniz, yeni bir tane oluşturmak List<Customer>, üzerinde yinelemek List<Object>, her öğeyi yeni listeye eklemek ve onu iade etmektir.


1

Başkalarının da belirttiği gibi, a List<Object>, a olmadığı için onları savurgan bir şekilde kullanamazsınız List<Customer>. Yapabileceğiniz şey, listede yerinde tür denetimi yapan bir görünüm tanımlamaktır. Google Koleksiyonlarını kullanarak :

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

Yukarıdaki Bozho ile benzer. Bu yöntemle (kendimden hoşlanmasam da) burada bazı geçici çözümler yapabilirsiniz:

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

Evet. Listenizi talep ettiğiniz jenerik türüne aktaracaktır.

Yukarıdaki verilen durumda, aşağıdaki gibi bazı kodlar yapabilirsiniz:

    List<Object> list = getList();
    return convert(list, new Customer());

Bu çözümü beğendim. SuppressWarnings eklemeniz gerekse bile, her güvenli olmayan döküm yerine tek bir yerde eklemek daha iyidir.
robson

1

Listeyle ne yapmak istediğinize bağlı olarak, onu bir List<Customer>. Listeye yalnızca Customernesne eklemek istiyorsanız , bunu aşağıdaki gibi bildirebilirsiniz:

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

Bu yasal (evet, sadece yasal değil, doğru - liste "Müşteriye bir tür süper tiptir") ve listeyi yalnızca listeye nesneler ekleyecek bir yönteme geçirecekseniz, o zaman yukarıdaki bunun için genel sınırlar yeterlidir.

Öte yandan, listeden nesneleri almak ve onları güçlü bir şekilde Müşteri olarak yazmak istiyorsanız - o zaman şansınız kalmaz ve haklı olarak öyle. Liste, List<Object>içeriklerin müşteri olduğuna dair bir garanti olmadığından, geri alma konusunda kendi dökümünüzü sağlamanız gerekir. (Ya da gerçekten, kesinlikle, iki kat emin olun ki, listenin yalnızca Customersdiğer cevaplardan birinden bir çift örnek içerecek ve kullanacaktır, ancak bu konuda jeneriklerden elde ettiğiniz derleme zamanı tür güvenliğini tamamen alt ettiğinizi fark edin. durum).

Genel olarak konuşursak, bir yöntem yazarken kabul edilebilecek mümkün olan en geniş genel sınırları dikkate almak her zaman iyidir, eğer bir kütüphane yöntemi olarak kullanılacaksa iki katına çıkar. Yalnızca bir listeden okuyacaksanız , örneğin List<? extends T>yerine kullanın List<T>- bu, arayanlara iletebilecekleri argümanlarda çok daha fazla kapsam sağlar ve sizin adınıza benzer önlenebilir sorunlarla karşılaşma olasılıklarının daha düşük olduğu anlamına gelir. burada yaşıyoruz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.