Liste, Liste <?>, Liste <T>, Liste <E> ve Liste <Object> arasındaki fark


194

Arasındaki farklar nelerdir List, List<?>, List<T>, List<E>, ve List<Object>?

1. Liste

List: ham bir türdür, bu nedenle değildir typesafe. Yalnızca döküm kötü olduğunda bir çalışma zamanı hatası oluşturur. Oyuncular kötü olduğunda derleme zamanı hatası istiyoruz. Kullanılması önerilmez.

2. Liste <?>

List<?>sınırsız bir joker karakterdir. Ama bunun ne için olduğundan emin değilim? Sorunsuz bir şekilde yazdırabilirim List<?>:

public static void test(List<?> list){
    System.out.println(list);   // Works
}

Bir öğeye neden öğe ekleyemiyorum List<?>?

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}

3. <T> listesi

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}

Bu sözdizimini anlamıyorum. Böyle bir şey gördüm ve işe yarıyor:

public <T> T[] toArray(T[] a){
    return a;   
}

Bazen, görüyorum <T>, ya <E>, veya <U>, <T,E>. Hepsi aynı mı yoksa farklı bir şey mi temsil ediyorlar?

4. <Nesne>

Bu, "Yöntem test(List<Object>)bağımsız değişken için geçerli değil " hatasını verir List<String>:

public static void test(List<Object> list){
    System.out.println(list);
}

Eğer bunu denemek o zaman "dan List<String>için atılamaz List<Object>" var:

test((List<Object>) names);

Kafam karıştı. Stringbir alt sınıf Object, neden List<String>bir alt sınıf değil List<Object>?

Yanıtlar:


77

1) Doğru

2) Bunu, öğenin türünü umursamadığınız "salt okunur" liste olarak düşünebilirsiniz. Örneğin, listenin uzunluğunu döndüren bir yöntemle kullanılabilir.

3) T, E ve U aynıdır, ancak insanlar örneğin tip için T, Eleman için E, değer için V ve anahtar için K kullanma eğilimindedir. Derleyen yöntem, belirli bir türde bir dizi aldığını ve aynı türde bir dizi döndürdüğünü söylüyor.

4) Portakal ve elmaları karıştıramazsınız. Bir dize listesini nesne listelerini bekleyen bir yönteme geçirebiliyorsanız Dize listenize bir Nesne ekleyebilirsiniz. (Ve tüm nesneler dize değildir)


2
Tarihinde salt okunur liste için +1 2. Bunu göstermek için bazı kodlar yazıyorum 2. tyvm
Thang Pham

İnsanlar neden kullanmalı List<Object>?
Thang Pham

3
Bu, her tür öğeyi kabul eden bir liste oluşturmanın bir yoludur, nadiren kullanırsınız.
Kaj

Aslında şu anda bir tanımlayıcıya sahip olamayacağınızı düşünerek kimsenin kullanacağını düşünmüyorum.
if_zero_equals_one

1
@ if_zero_equals_one Evet, ancak derleyici uyarısı alırsınız (uyarır ve ham türleri kullandığınızı söyler) ve kodunuzu asla uyarılarla derlemek istemezsiniz.
Kaj

26

Son bölüm için: String, Object'in bir alt kümesi olmasına rağmen, List <String> <Object> Listesinden devralınmamıştır.


11
Çok iyi bir nokta; birçoğu C sınıfı P sınıfından miras aldığından, List <C> 'nin de List <P>' den miras aldığını varsayar. Belirttiğiniz gibi durum böyle değil. Bunun nedeni, <String> Listesinden <Object> Listesine yayınlayabilirsek, Nesneleri bu listeye koyabilir ve böylece bir öğeyi almaya çalışırken <String> Listesinin orijinal sözleşmesini ihlal edebiliriz.
Peter

2
+1. İyi bir nokta. Öyleyse insanlar neden kullanacaklar List<Object>?
Thang Pham

9
<Object> listesi, farklı sınıflardaki nesnelerin bir listesini saklamak için kullanılabilir.
Farshid Zaker

20

Gösterim List<?>"bir şeyin listesi (ama ne demiyorum)" anlamına gelir. İçindeki kod testlistedeki herhangi bir nesne için çalıştığından, bu resmi bir yöntem parametresi olarak çalışır.

Bir type parametresi kullanmak (3. noktanızda olduğu gibi), type parametresinin bildirilmesini gerektirir. Bunun için Java sözdizimi <T>işlevin önüne koymaktır . Bu, yöntem gövdesindeki adları kullanmadan önce bir yönteme resmi parametre adlarını bildirmeye tam olarak benzer.

A'yı List<Object>kabul etmemeye gelince List<String>, bu mantıklıdır çünkü a Stringdeğildir Object; bir alt sınıfıdır Object. Çözüm beyan etmektir public static void test(List<? extends Object> set) .... Ama sonra extends Objectgereksizdir, çünkü her sınıf doğrudan veya dolaylı olarak uzanır Object.


İnsanlar neden kullanmalı List<Object>?
Thang Pham

10
Bence "bir şeylerin listesi" daha iyi bir anlamdır List<?>çünkü liste belirli ama bilinmeyen tiptedir. List<Object>gerçekten "her şeyin bir listesi" olurdu, çünkü gerçekten bir şey içerebilir.
ColinD

1
@ColinD - "Herhangi bir şey" anlamında "her şey" demek istedim. Ama sen haklısın; "bir şeyin listesi, ama size ne olduğunu söylemeyeceğim" anlamına gelir.
Ted Hopp

@ColinD neden sözlerini tekrarladın demekti? evet biraz farklı kelimelerle yazılmış, ama anlamı aynı ...
user25

14

Eğer döküm olamaz nedeni List<String>ile List<Object>bunu kısıtlarını ihlal olanak sağlayacak olmasıdır List<String>.

Aşağıdaki senaryoyu düşünün: Eğer varsa List<String>, yalnızca tür nesneler içermesi gerekir String. (Bu bir finalsınıftır)

Eğer bunu a'ya dökebilirsem, o listeye List<Object>eklememe izin veririm Object, böylece orijinal sözleşmesini ihlal ederim List<String>.

Böylece, genel olarak, sınıf sınıftan Cmiras alırsa P, bunun GenericType<C>da miras olduğunu söyleyemezsiniz GenericType<P>.

Not Zaten bu konuda daha önceki bir cevapta yorum yaptım ama daha da büyümek istedim.


tyvm, hem yorumunuzu hem de cevabınızı yüklüyorum, çünkü bu çok iyi bir açıklama. Şimdi insanlar nerede ve neden kullanırdı List<Object>?
Thang Pham

3
Genellikle List<Object>jeneriklerin amacını bozguna uğrattığı için kullanmamalısınız. Ancak, eski kodun Listfarklı türleri kabul edebileceği durumlar vardır , bu nedenle ham türler için derleyici uyarılarından kaçınmak için kodu tür parametrelemesini kullanmak üzere uyarlamak isteyebilirsiniz. (Ancak işlevsellik değişmedi)
Peter


5

Java tarihi bağlamında bunlar hakkında konuşalım;

  1. List:

Liste, herhangi bir Nesne içerebileceği anlamına gelir. Liste Java 5.0'dan önceki sürümdeydi; Java 5.0 geriye dönük uyumluluk için List'i tanıttı.

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>:

?bilinmeyen Nesne anlamına gelir herhangi bir Nesne değil; joker karakter ?tanıtımı Genel Tür tarafından oluşturulan sorunu çözmek içindir; joker karakterlere bakın ; ancak bu da başka bir soruna neden olur:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

Proje Lib'inizde hiçbir T veya E türünün öncülünde genel Beyan anlamına gelir.

  1. List< Object> "Genel jenerik parametreleme" anlamına gelir.

5

Üçüncü noktanızda "T" çözülemez çünkü bildirilmez, genellikle genel bir sınıf bildirdiğinizde bağlı tip parametresinin adı olarak "T" kullanabilirsiniz , oracle öğreticileri de dahil olmak üzere birçok çevrimiçi örnek "T" type parametresinin adı, örneğin, şöyle bir sınıf bildirdiğinizi varsayalım:

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

FooHandler's operateOnFooYöntemin sınıf bildiriminin kendisinde bildirilen "T" türünde bir değişken beklediğini söylüyorsunuz.

public void operateOnFoos(List<T> foos)

T, E veya U tüm durumlarda type parametresinin tüm tanımlayıcıları sözdizimini kullanan birden fazla tip parametresine bile sahip olabilirsiniz.

public class MyClass<Atype,AnotherType> {}

etkili bir şekilde Sting bir alt nesne türü olmasına rağmen dördüncü ponintinizde, jenerik sınıflarında böyle bir ilişki yoktur , derleyici bakış açısından iki farklı türden List<String>bir alt tür değildir List<Object>, bu en iyi bu blog girişinde açıklanmıştır


5

teori

String[] yayınlanabilir Object[]

fakat

List<String>kullanılamaz List<Object>.

Uygulama

Listeler için bundan daha incedir, çünkü derleme zamanında bir yönteme iletilen bir List parametresinin türü kontrol edilmez. Yöntem tanımı da söylenebilir List<?>- derleyicinin bakış açısından eşdeğerdir. Bu nedenle OP'nin # 2 numaralı örneği, derleme hataları değil çalışma zamanı hataları verir.

List<Object>Bir yönteme iletilen bir parametreyi dikkatle ele alırsanız, listenin herhangi bir öğesinde tür denetimini zorlamıyorsanız, yönteminizi kullanarak tanımlanmış olabilir, List<Object>ancak aslında List<String>çağrı kodundan bir parametre kabul edebilirsiniz .

C. Yani bu kod derleme veya çalışma zamanı hataları vermeyecek ve aslında (ve şaşırtıcı bir şekilde?) Çalışacaktır:

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test(argsList);  // The object passed here is a List<String>
}

public static void test(List<Object> set) {
    List<Object> params = new ArrayList<>();  // This is a List<Object>
    params.addAll(set);       // Each String in set can be added to List<Object>
    params.add(new Long(2));  // A Long can be added to List<Object>
    System.out.println(params);
}

B. Bu kod bir çalışma zamanı hatası verecektir:

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test1(argsList);
    test2(argsList);
}

public static void test1(List<Object> set) {
    List<Object> params = set;  // Surprise!  Runtime error
}

public static void test2(List<Object> set) {
    set.add(new Long(2));       // Also a runtime error
}

C. Bu kod bir çalışma zamanı hatası verecektir ( java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]):

public static void main(String[] args) {
    test(args);
}

public static void test(Object[] set) {
    Object[] params = set;    // This is OK even at runtime
    params[0] = new Long(2);  // Surprise!  Runtime error
}

B'de, parametre derleme zamanında setyazılmaz List: derleyici bunu olarak görür List<?>. Bir çalışma zamanı hatası var çünkü çalışma zamanında, setiletilen gerçek nesne haline gelir main()ve bu bir List<String>. A List<String>kullanılamaz List<Object>.

C de, parametre bir setgerektirir Object[]. String[]Parametre olarak bir nesne ile çağrıldığında derleme hatası ve çalışma zamanı hatası yoktur . Çünkü String[]oyuncu kadrosu Object[]. Ama aldığı gerçek nesne test()değişmedi String[], değişmedi. Böylece paramsnesne de bir olur String[]. Ve a öğesinin 0 öğesi String[]a Long!

(Umarım burada her şeyim var, akıl yürütmem yanlışsa, toplumun bana söyleyeceğinden eminim. GÜNCELLENDİ: Örnek A'daki kodu güncelledik, böylece yapılan noktayı göstermeye devam ediyor.


O senin örnek A denedim gelmez çalışır: List<Object> cannot be applied to List<String>. Sen olamaz geçmesine ArrayList<String>bekleyen yönteme ArrayList<Object>.
parsecer

Teşekkürler, tarihin oldukça geç örnek A şimdi işe yarayacak şekilde tweaked. Ana değişiklik argsList'i genel olarak main () olarak tanımlamaktı.
radfast

4

Sorun 2, çünkü "System.out.println (set);" "System.out.println (set.toString ())" anlamına gelir; set bir List örneğidir, bu nedenle complier List.toString () öğesini çağırır;

public static void test(List<?> set){
set.add(new Long(2)); //--> Error  
set.add("2");    //--> Error
System.out.println(set);
} 
Element ? will not promise Long and String, so complier will  not accept Long and String Object

public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2");    //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object

Sorun 3: Bu semboller aynı, ancak onlara farklı özellikler verebilirsiniz. Örneğin:

public <T extends Integer,E extends String> void p(T t, E e) {}

Sorun 4: Toplama tip parametresi kovaryansına izin vermiyor. Ancak dizi kovaryansa izin verir.


0

Haklısın: String, Object'in bir alt kümesidir. String, Object'den daha "kesin" olduğundan, System.out.println () için bir argüman olarak kullanmak üzere onu yayınlamanız gerekir.

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.