Java'da genel bir dizi nasıl oluşturulur?


1090

Java jeneriklerinin uygulanması nedeniyle, aşağıdaki gibi bir kodunuz olamaz:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

Tip güvenliğini korurken bunu nasıl uygulayabilirim?

Java forumlarında şöyle bir çözüm gördüm:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Ama gerçekten olanları anlamıyorum.


14
Burada gerçekten bir dizi kullanmanız gerekiyor mu? Koleksiyon kullanmaya ne dersiniz?
matt b

12
Evet, koleksiyonların bu sorun için daha zarif olduğunu da düşünüyorum. Ancak bu bir sınıf ödevi içindir ve gereklidir :(
tatsuhirosatou

3
Neden burada yansıtmak gerektiğini anlamıyorum.Java gramer garip: gibi yeni java.util.HashMap <String, String> [10] geçerli değil. yeni java.util.HashMap <uzun, uzun> (10) geçerli değil. new long [] [10] geçerli değil, new long [10] [] geçerli. Bu şeyler java programı yazabilen bir program yazmayı yapar gibi görünüyor daha zor.
bronz adam

Yanıtlar:


703

Karşılığında bir soru sormam gerekiyor: GenSet"kontrol edildi" veya "kontrol edilmedi" mi? Bu ne anlama geliyor?

  • İşaretli : güçlü yazma . GenSet(yani onun yapıcı açıkça ile denirdi içerdiği nesnelerin ne tür açıkça bilir Class<E>argüman ve onlar türünde olmayan argümanlar geçirildiğinde yöntemleri bir özel durum oluşturur E. Bkz Collections.checkedCollection.

    -> bu durumda şunları yazmalısınız:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • İşaretlenmemiş : zayıf yazma . Bağımsız değişken olarak iletilen nesnelerin hiçbirinde tür denetimi yapılmaz.

    -> bu durumda, yazmalısınız

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    Dizinin bileşen türünün type parametresinin silinmesi gerektiğini unutmayın :

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

Bütün bunlar, Java'daki jeneriklerin bilinen ve kasıtlı bir zayıflığından kaynaklanmaktadır: silme kullanılarak uygulanmıştır, bu nedenle "genel" sınıflar, çalışma zamanında hangi tür bağımsız değişkenle oluşturulduklarını bilmezler ve bu nedenle açık bir mekanizma (tip kontrolü) uygulanmadıkça güvenlik.


7
Performans açısından en iyi seçenek ne olurdu? Ben oldukça sık (bir döngü içinde) bu diziden öğeleri almak gerekiyor. Yani bir koleksiyon muhtemelen daha yavaştır, ancak bu ikisinden hangisi en hızlıdır?
user1111929

3
Genel tür sınırlandırılmışsa, destekleme dizisi sınırlayıcı türde olmalıdır.
Mordechai

5
@AaronDigulla Sadece bunun bir atama değil, yerel bir değişkenin başlatılması olduğunu açıklığa kavuşturmak için. Bir ifadeye / ifadeye açıklama ekleyemezsiniz.
kennytm

1
@Varkhan Bu dizileri sınıf uygulaması içinde yeniden boyutlandırmanın bir yolu var mı? Örneğin, ArrayList gibi taşma işleminden sonra yeniden boyutlandırmak istersem. Object[] EMPTY_ELEMENTDATA = {}Depolama için sahip oldukları ArrayList uygulamasına baktım . Bu mekanizmayı, jenerik ilaç kullanarak türü bilmeden yeniden boyutlandırmak için kullanabilir miyim?
JourneyMan

2
Jenerik tipte bir yöntem yapmak isteyenler için (aradığım şey buydu), şunu kullanın:public void <T> T[] newArray(Class<T> type, int length) { ... }
Daniel Kvist

225

Bunu yapabilirsiniz:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

Bu, Etkili Java'da genel bir koleksiyon uygulamanın önerilen yollarından biridir ; Madde 26 . Tür hatası yok, diziyi tekrar tekrar yayınlamaya gerek yok. Bununla birlikte, bu potansiyel olarak tehlikeli olduğu ve dikkatle kullanılması gerektiği için bir uyarıyı tetikler. Yorumlarda ayrıntılı olarak açıklandığı gibi, bu Object[]artık bizim türümüz olarak maskeleniyor E[]ve güvenli bir şekilde ClassCastExceptionkullanılmadığında beklenmedik hatalara veya hatalara neden olabilir .

Genel bir kural olarak, döküm dizisi dahili olarak kullanıldığı sürece (örn. Bir veri yapısını yedeklemek için) ve istemci koduna döndürülmediği veya açıklanmadığı sürece bu davranış güvenlidir. Genel türden bir diziyi başka bir koda döndürmeniz gerekiyorsa, Arraybahsettiğiniz yansıma sınıfı gitmek için doğru yoldur.


Mümkün olan her yerde, Listjenerikler kullanıyorsanız dizilerden ziyade s ile çalışmaktan çok daha mutlu olacağınızı belirtmek gerekir . Elbette bazen bir seçeneğiniz yoktur, ancak koleksiyon çerçevesini kullanmak çok daha sağlamdır.


47
Dizi, String[] s=b;yukarıdaki test()yöntemde olduğu gibi herhangi bir türdeki bir dizi olarak ele alınırsa, bu çalışmaz . Çünkü E dizisi gerçekten değil, Object []. İsterseniz bu önemlidir, örneğin a List<String>[]- bunun için bir kullanamazsınız Object[], List[]özel olarak sahip olmanız gerekir . Bu yüzden yansıtılan Class <?> Dizi oluşturma özelliğini kullanmanız gerekir.
Lawrence Dol

8
Köşe / sorun, örneğin public E[] toArray() { return (E[])internalArray.clone(); }ne zaman internalArrayyazıldığını E[]ve dolayısıyla aslında bir Object[]. Bu tür bir tür dökme istisna ile çalışma zamanında başarısız olur, çünkü Object[]ne tür Eolursa olsun bir diziye atanamaz .
Lawrence Dol

17
Temel olarak, bu yaklaşım, diziyi döndürmediğiniz veya geçirmediğiniz veya sınıfın dışındaki belirli bir türde bir dizi gerektiren bir yerde sakladığınız sürece çalışır. Sınıfın içinde olduğunuz sürece, iyisiniz çünkü E siliniyor. "Tehlikeli" çünkü geri dönmeye çalışırsanız, güvensiz olduğuna dair hiçbir uyarı almazsınız. Ancak dikkatli olursanız işe yarar.
newacct

3
Oldukça güvenlidir. In E[] b = (E[])new Object[1];size açıkça oluşturulan diziye tek referans olduğunu görebiliriz bve türü olduğunu bolduğunu E[]. Bu nedenle, aynı diziye farklı türden farklı bir değişken aracılığıyla yanlışlıkla erişme tehlikesi yoktur. Bunun yerine, Object[] a = new Object[1]; E[]b = (E[])a; nasıl kullandığınız konusunda paranoyak olmanız gerekirdi a.
Aaron McDaid

5
En azından Java 1.6'da bu bir uyarı oluşturur: "Object [] 'den T []' ye
işaretlenmemiş yayın

61

Tip güvenliğini korurken tam olarak aradığınız türde bir dizi elde etmek için jenerikleri nasıl kullanacağınız (diğer cevapların aksine, size bir Objectdizi geri verir veya derleme zamanında uyarılara neden olur):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

Bu, uyarılar olmadan derler ve gördüğünüz gibi main, bir örnek GenSetolarak bildirdiğiniz herhangi bir tür için a, bu tür bir diziye atayabilir ve bu türdeki abir değişkene öğeyi atayabilirsiniz , yani dizi ve dizideki değerler doğru tiptedir.

Java Öğreticileri'nde tartışıldığı gibi, sınıf değişmezlerini çalışma zamanı belirteci olarak kullanarak çalışır . Sınıf değişmezleri derleyici tarafından örnek olarak ele alınır java.lang.Class. Birini kullanmak için ile birlikte bir sınıfın adını takip edin .class. Yani, sınıfı temsil eden String.classbir Classnesne gibi davranır String. Bu aynı zamanda arayüzler, numaralandırmalar, herhangi bir boyutlu diziler (örn. String[].class), İlkel öğeler (ör. int.class) Ve anahtar kelime void(ör. void.class) İçin de geçerlidir.

Classkendisi (ilan geneldir Class<T>, burada Tbu tür için standları Classnesne temsil etmektedir) türü, yani String.classbir Class<String>.

Bu nedenle, yapıcıyı her çağırdığınızda, örneğin bildirilen türünün (örn. İçin ) GenSetbir dizisini temsil eden ilk bağımsız değişken için bir sınıf değişmezi iletirsiniz . İlkel değerler tür değişkenleri için kullanılamayacağından, bir dizi temel öğe elde edemeyeceğinizi unutmayın.GenSetString[].classGenSet<String>

Yapıcı içinde, yöntemin çağrılması, castiletilen iletilen Objectargümanı Classyöntemin çağrıldığı nesnenin temsil ettiği sınıfa döndürür . Statik yöntemi çağırma newInstanceiçinde java.lang.reflect.Arraybir şekilde geri dönüş Objectile temsil edilen tipte bir dizi Classilk bağımsız değişken olarak ve belirtilen uzunlukta geçirilen nesne intikinci bağımsız değişken olarak geçti. Yöntem, arama getComponentTypebir döner Classdizi bileşen tipi ifade eden nesne ile temsil edilen Class(örneğin, yöntem çağrıldığı nesne String.classiçin String[].class, nulleğer Classnesne bir dizi temsil etmez).

Bu son cümle tam olarak doğru değil. Çağırma sınıfı temsil eden String[].class.getComponentType()bir Classnesne döndürür String, ancak türü Class<?>değil Class<String>, bu nedenle aşağıdaki gibi bir şey yapamazsınız.

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Aynı şey, Classbir Classnesneyi döndüren her yöntem için de geçerlidir .

Joachim Sauer'ın bu cevaba ilişkin yorumu ile ilgili olarak (kendim hakkında yorum yapmak için yeterli üne sahip değilim), cast'ı kullanan örnek T[]bir uyarı ile sonuçlanacaktır, çünkü derleyici bu durumda tip güvenliği garanti edemez.


Ingo'nun yorumlarıyla ilgili düzenleme:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

5
Bu işe yaramaz, yeni String [...] yazmanın sadece karmaşık bir yoludur. Ama gerçekten ihtiyaç duyulan şey, kamu statik <T> T [] newArray (int size) {...} gibi bir şeydir ve bu sadece java noir'de mevcut değildir, yansıma ile simüle edilebilir - bunun nedeni, genel tür somutlaştırılır, çalışma zamanında kullanılamaz.
Ingo

4
@Ingo Neden bahsediyorsun? Kodum herhangi bir türde bir dizi oluşturmak için kullanılabilir.
gdejohn

3
@Charlatan: Tabii ama yeni de olabilir]. Soru şudur: türü ve ne zaman kim bilir. Bu nedenle, sahip olduğunuz tek şey genel bir türse yapamazsınız.
Ingo

2
Bundan şüphe etmiyorum. Mesele şu ki, genel tip X için çalışma zamanında bir Class nesnesi alamıyorsunuz.
Ingo

2
Neredeyse. Bunun yeni [] ile yapılabileceklerden daha fazlası olduğunu kabul ediyorum. Pratikte, bu neredeyse her zaman işi yapacak. Bununla birlikte, örneğin E [] toArray () yöntemine sahip olan ve gerçekten de gerçek bir E [] dizisi döndüren E ile parametreli bir kapsayıcı sınıf yazmak hala mümkün değildir. Kodunuz yalnızca koleksiyonda en az bir E nesnesi olduğunda uygulanabilir. Bu nedenle, genel bir çözüm mümkün değildir.
Ingo

42

Güvenli tipte olan tek cevap budur

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

Ben bakmak zorunda kaldı, ama evet, ikinci "uzunluk" argüman Arrays#copyOf()ilk argüman olarak verilen dizi uzunluğundan bağımsızdır. Bu zekice, ancak çağrıların maliyetini ödüyor Math#min()ve System#arrayCopy()her ikisi de bu işi yapmak için kesinlikle gerekli değil. docs.oracle.com/javase/7/docs/api/java/util/…
seh

8
EBir tür değişkeni ise bu çalışmaz . Varargs , bir type değişkeni Eolduğunda Ebir silme dizisi oluşturur , bu da onu çok farklı yapmaz (E[])new Object[n]. Lütfen http://ideone.com/T8xF91 adresine bakın . Öyle hiçbir şekilde başka bir cevap daha tip kasa.
Radiodef

1
@Radiodef - çözüm derleme zamanında tür güvenli. silme işleminin dil spesifikasyonunun bir parçası olmadığını unutmayın; gelecekte dikkatlice yazabilmemiz için spesifikasyon dikkatlice yazılmıştır - ve sonra bu çözüm diğer çözümlerin aksine çalışma zamanında da mükemmel çalışır.
ZhongYu

@Radiodef - Genel dizi oluşturmanın yasaklanmasının iyi bir fikir olup olmadığı tartışmalıdır. ne olursa olsun, dil bir arka kapı bırakıyor - vararg genel dizi oluşturma gerektirir. Dilin izin verdiği kadar iyidir new E[]. Örneğinizde gösterdiğiniz sorun, bu soruya ve bu yanıta özgü olmayan genel bir silme sorunudur.
ZhongYu

2
@Radiodef - Bazı farklılıklar var. Bu çözeltinin doğruluğu derleyici tarafından kontrol edilir; zorla atılmanın insan akıl yürütmesine dayanmaz. Bu sorun için fark önemli değildir. Bazı insanlar biraz süslü olmayı sever, hepsi bu. Herhangi biri OP'nin ifadesiyle yanlış yönlendirilirse, yorumlarınız ve benimki ile açıklığa kavuşturulur.
ZhongYu

33

Daha fazla boyuta genişletmek için, yalnızca []'s ve boyut parametrelerini ekleyin newInstance()( Tbir type parametresidir, clsa Class<T>, d1üzerinden d5tam sayılardır):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

Ayrıntılar Array.newInstance()için bakınız.


4
+1 Çok boyutlu dizi oluşturma ile ilgili bu gönderinin kopyaları olarak kapanan sorular vardı - ancak buna özel olarak cevap verilmemişti.
Paul Bellora

1
@JordanC Belki; ruhaniyet olarak stackoverflow.com/a/5671304/616460 ile aynı olmasına rağmen ; Yarın üstesinden gelmenin en iyi yolunu düşüneceğim. Uykum var.
Jason C

14

Java 8'de lambda veya yöntem referansı kullanarak bir tür genel dizi oluşturma yapabiliriz. Bu, yansıtıcı yaklaşıma (a Class) geçen benzerdir , ancak burada yansıma kullanmıyoruz.

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

Örneğin, bu tarafından kullanılır <A> A[] Stream.toArray(IntFunction<A[]>).

Bu , Java 8 öncesi anonim sınıflar kullanılarak da yapılabilir, ancak daha hantaldır.


Bunun gibi özel bir arayüze gerçekten ihtiyacınız yok ArraySupplier, yapıcıyı ilan edebilir GenSet(Supplier<E[]> supplier) { ...ve sahip olduğunuzla aynı satırla çağırabilirsiniz.
Lii

4
@Lii Örneğimle aynı olmak için, olurdu IntFunction<E[]>, ama evet bu doğru.
Radiodef

11

Bu, Etkili Java, 2. Baskı , madde 25, Bölüm 5 (Jenerikler) kapsamındadır. Diziler için listeleri tercih etme

Kodunuz, kontrol edilmemiş bir uyarı oluşturmasına rağmen (aşağıdaki ek açıklama ile bastırabileceğiniz) çalışır:

@SuppressWarnings({"unchecked"})

Ancak, bir Array yerine List kullanmak daha iyi olur.

OpenJDK proje sitesinde bu hata / özellik hakkında ilginç bir tartışma var .


8

Sınıf bağımsız değişkenini yapıcıya iletmeniz gerekmez. Bunu dene.

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

ve

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

sonuç:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

7

Java jenerikleri, derleme zamanında türleri denetleyerek ve uygun dökümler ekleyerek, ancak derlenen dosyalardaki türleri silerek çalışır . Bu, genel kütüphaneleri jenerikleri anlamayan (kasıtlı bir tasarım kararıdır) kodla kullanılabilir hale getirir, ancak normal olarak türün çalışma zamanında ne olduğunu bulamayacağınız anlamına gelir.

Kamu Stack(Class<T> clazz,int capacity)yapıcı aracı sınıf açıklamaları çalışma zamanında bir sınıf nesnesi, geçirmenizi gerektiren olduğunu ihtiyacı koduna çalışma zamanında kullanılabilir. Ve Class<T>form, derleyicinin geçtiğiniz Class nesnesinin tam olarak T türü için Class nesnesi olduğunu kontrol edeceği anlamına gelir. T'nin bir alt sınıfı değil, T'nin bir üst sınıfı değil, tam olarak T'dir.

Bu, kurucunuzda uygun türde bir dizi nesnesi oluşturabileceğiniz anlamına gelir; bu, koleksiyonunuzda depoladığınız nesnelerin türünün, koleksiyona eklendikleri noktada türlerini kontrol edeceği anlamına gelir.


6

Merhaba iplik ölü olmasına rağmen, buna dikkatinizi çekmek istiyorum:

Jenerik derleme zamanı sırasında tip kontrolü için kullanılır:

  • Bu nedenle amaç, gelen şeyin ihtiyacınız olan şey olduğunu kontrol etmektir.
  • Geri döndüğünüz şey tüketicinin ihtiyaç duyduğu şeydir.
  • Şuna göz at:

resim açıklamasını buraya girin

Genel sınıf yazarken yazım uyarıları konusunda endişelenmeyin. Kullanırken endişelen.


6

Bu çözüm ne olacak?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

Çalışıyor ve gerçek olamayacak kadar basit görünüyor. Herhangi bir dezavantajı var mı?


3
Düzgün, ancak sadece 'manuel' olarak adlandırırsanız, yani öğeleri tek tek geçirdiğinizde çalışır. Yeni bir örneği oluşturamazsanız , işleve geçmek için T[]programlı olarak bir a T[] elemsoluşturamazsınız. Ve yapabilseydiniz, bu işleve ihtiyacınız olmazdı.
orlade

5

Bu koda da bakınız:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

Her türden nesnenin listesini aynı türden bir diziye dönüştürür.


Evet, null değerini döndürürsünüz, bu beklenen boş dizi değildir. Yapabileceğinizin en iyisi, ama ideal değil.
Kevin Cox

Bu, Listiçinde birden fazla nesne türü varsa, örneğin toArray(Arrays.asList("abc", new Object()))atılacaksa da başarısız olabilir ArrayStoreException.
Radiodef

Bunun soyulmuş bir versiyonunu kullandım; ilk çalıştığım şey işe yaradı, ama itiraf etmek gerekirse ben daha ilgili bazı çözümler denemedim. Her dizin için aynı değeri istediğimden beri forkullandığım döngü ve diğerlerini önlemek Arrays.fill(res, obj);için.
bbarker

5

Benim için işe yarayan hızlı ve kolay bir yol buldum. Ben sadece Java JDK 8 bu kullandığınızı unutmayın. Önceki sürümleri ile çalışacak mı bilmiyorum.

Belirli bir tür parametresinin genel bir dizisini başlatamasak da, önceden oluşturulmuş bir diziyi genel bir sınıf yapıcısına geçirebiliriz.

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

Şimdi ana diziyi şöyle oluşturabiliriz:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

Dizilerinizde daha fazla esneklik için bağlantılı bir liste kullanabilirsiniz. ArrayList ve Java.util.ArrayList sınıfında bulunan diğer yöntemler.


4

Örnek bir dizi oluşturmak için Java yansımasını kullanmaktır. Bunu yapmak, genellikle güvenli olmadığından önerilmez. Bunun yerine, yapmanız gereken sadece dahili bir Liste kullanmak ve diziden kaçınmaktır.


13
İkinci örnek (Array.newInstance kullanılarak ()) olup aslında türgüvenli içinde. Class nesnesinin T türünün dizinin T değeriyle eşleşmesi gerektiğinden bu mümkündür. Temel olarak, Java çalışma zamanının jenerikler için attığı bilgileri sağlamaya zorlar.
Joachim Sauer

4

Değerler listesi iletiliyor ...

public <T> T[] array(T... values) {
    return values;
}

3

Basit bir otomatik test yardımcı programı için geçirilen bir sınıfı yansıtacak şekilde bu kod snippet'i yaptım.

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

Bu segmente dikkat edin:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

burada Array.newInstance (dizi sınıfı, dizi boyutu) başlatmak için dizi . Sınıf hem ilkel (int.class) hem de nesne (Integer.class) olabilir.

BeanUtils, Spring'in bir parçasıdır.


3

Aslında bunu yapmanın daha kolay bir yolu, bir nesne dizisi oluşturmak ve onu aşağıdaki örnekte olduğu gibi istediğiniz türe dönüştürmektir:

T[] array = (T[])new Object[SIZE];

burada SIZEbir sabit ve Tbir tür tanımlayıcısıdır


1

Başkaları tarafından önerilen zorla oyuncu seçimi benim için işe yaramadı, yasadışı döküm istisnası attı.

Ancak, bu örtük oyuncular işe yaradı:

Item<K>[] array = new Item[SIZE];

Burada Öğe, üyeyi içeren tanımladığım bir sınıftır:

private K value;

Bu şekilde K türünden bir dizi (öğenin yalnızca değeri varsa) veya sınıf Öğesinde tanımlanmasını istediğiniz herhangi bir genel türü elde edersiniz.


1

Yayınladığınız örnekte neler olup bittiği sorusunu başka kimse yanıtlamadı.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Diğerlerinin söylediği gibi, jenerikler derleme sırasında "silinir". Yani çalışma zamanında bir jenerik örneği bileşen türünün ne olduğunu bilmiyor. Bunun nedeni tarihseldir, Sun varolan arayüzü (hem kaynak hem de ikili) bozmadan jenerikler eklemek istedi.

Öte yandan Diziler do zamanında bileşen tipini biliyorum.

Bu örnek, yapıcıyı çağıran kodun (türü bilen) sınıfa gerekli türü bildiren bir parametre iletmesini sağlayarak bu soruna geçici bir çözüm sağlar.

Böylece uygulama sınıfı benzer bir şeyle

Stack<foo> = new Stack<foo>(foo.class,50)

ve kurucu artık (çalışma zamanında) bileşen türünün ne olduğunu bilir ve yansıma API'sı aracılığıyla diziyi oluşturmak için bu bilgileri kullanabilir.

Array.newInstance(clazz, capacity);

Son olarak, bir tür dökümcemiz var çünkü derleyicinin döndürdüğü dizinin Array#newInstance()doğru tip olduğunu bilmemizin bir yolu yok (bilsek bile).

Bu stil biraz çirkin ama bazen herhangi bir nedenle çalışma zamanında bileşen türlerini bilmeleri gereken genel türler oluşturmak için en az kötü çözüm olabilir (diziler oluşturmak veya bileşen türlerinin örneklerini oluşturmak, vb.).


1

Bu soruna geçici bir çözüm buldum.

Aşağıdaki satır genel dizi oluşturma hatası veriyor

List<Person>[] personLists=new ArrayList<Person>()[10];

Ancak ben List<Person>ayrı bir sınıfta kapsüllemek , çalışır.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

PersonList sınıfındaki kişileri bir alıcıya gösterebilirsiniz. Aşağıdaki satır List<Person>, her öğede bir içeren bir dizi verecektir . Başka bir deyişle List<Person>.

PersonList[] personLists=new PersonList[10];

Ben üzerinde çalışıyordu bazı kod böyle bir şey gerekiyordu ve bu işe almak için yaptım. Şimdiye kadar sorun yok.


0

Bir Object dizisi oluşturabilir ve onu her yerde E'ye yayınlayabilirsiniz. Evet, bunu yapmanın çok temiz bir yolu değil ama en azından işe yaramalı.


"Bazı açıklama ve bağlam sağlayan uzun cevaplar arıyoruz. Sadece tek satırlık bir cevap vermeyin; cevabınızın neden doğru olduğunu, ideal olarak alıntılarla açıklayın. Açıklama yapmadan cevaplar kaldırılabilir."
gparyani

BU, genel sınıfınız Karşılaştırılabilir arabirimi uygulamak istiyorsa gibi bazı durumlarda çalışmaz.
RamPrasadBismil

Sanırım yedi yıl önce hoş geldiniz.
Esko

1
Diziyi genel koddan genel olmayan bir arayana döndürmeye çalışırsanız bu çalışmaz. Baş döndürücü bir sınıf senaryosu olacak.
plugwash

0

bunu dene.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

Kodunuzu çalıştıramıyorum, Elementsınıfınız nereden geliyor?

0

Buna rağmen dağınık bir çözüm, ana sınıfınızın içine ikinci bir "tutucu" sınıf yerleştirmek ve bunu verilerinizi tutmak için kullanmak olacaktır.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

3
Bu aslında işe yaramıyor. new Holder<Thing>[10]genel dizi oluşturmadır.
Radiodef

0

Belki bu soru ile ilgisi yok ama ben generic array creationkullanmak için " " hatası alırken

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

Aşağıdaki çalışmaları buluyorum (ve benim için çalıştım) @SuppressWarnings({"unchecked"}):

 Tuple<Long, String>[] tupleArray = new Tuple[10];

Evet, bu oldukça ilgili değil, aynı sorunlara dayanıyor (silme, dizi kovaryansı). Parametrelenmiş tipte diziler oluşturma hakkında bir gönderi örneği: stackoverflow.com/questions/9542076/…
Paul Bellora

0

Bu kod etkili bir genel dizi oluşturmak merak ediyorum?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

Düzenleme: Belki böyle bir dizi oluşturmak için alternatif bir yol, eğer gerekli boyutu biliniyordu ve küçük, sadece "null" ler gerekli sayıda sıfırArray komutuna beslemek olurdu?

Açıkçası bu createArray kodunu kullanmak kadar çok yönlü değildir.


Hayır, bu işe yaramıyor. Varargs bir tip değişkeni Tolduğunda silme işlemini oluşturur T, yani zeroArraybir Object[]. Bkz. Http://ideone.com/T8xF91 .
Radiodef

0

Bir oyuncu kadrosu kullanabilirsiniz:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

Bunu önerecek olursanız, gerçekten sınırlarını açıklamanız gerekir. Asla asınıfın dışında kalmayın!
Radiodef

0

Aslında genel bir dizi başlatmak için yetersizliği atlamak için oldukça benzersiz bir çözüm buldum. Yapmanız gereken, genel değişken T'yi bu şekilde alan bir sınıf oluşturmaktır:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

ve sonra dizi sınıfınızda sadece şöyle başlamasını sağlayın:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

a başlatmak new Generic Invoker[], işaretlenmemiş bir soruna neden olur, ancak aslında herhangi bir sorun olmamalıdır.

Diziden almak için şu şekilde [i] .variable dizisini çağırmalısınız:

public T get(int index){
    return array[index].variable;
}

Diziyi yeniden boyutlandırma gibi geri kalanı Arrays.copyOf () ile şu şekilde yapılabilir:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

Ve add fonksiyonu şu şekilde eklenebilir:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

1
Soru, Tparametreli tipte bir dizi değil , genel tip parametresi türünde bir dizi oluşturmaktı.
Sotirios Delimanolis

Yine de aynı görevi tamamlar ve özel koleksiyonunuzun kullanımını kolaylaştırmak için bir sınıfa girmenizi gerektirmez.
Yengeç Bulutsusu

Ne görevi ? Kelimenin tam anlamıyla farklı bir görevdir: paramaterize edilmiş bir tür dizisi ile genel tür parametresinin dizisi.
Sotirios Delimanolis

Genel bir türden bir dizi oluşturmanıza izin verir? Asıl sorun, benim yöntemimi kullanarak kullanıcının bir sınıfa girmesine veya bir dizeye bir nesne yayınlamaya çalışmak gibi denetlenmeyen bir hata vermenize gerek kalmadan genel bir tür kullanarak bir dizi başlatmak oldu. Chill gibi, yaptığım işte en iyisi değilim ve programlama için okula gitmedim, ancak internetteki başka bir çocuk tarafından söylenmekten ziyade hala küçük bir girişi hak ettiğimi düşünüyorum.
Yengeç Bulutsusu

Sotiros'a katılıyorum. Cevabı düşünmenin iki yolu var. Ya farklı bir sorunun cevabı ya da soruyu genelleme girişimidir. Her ikisi de yanlış / yararlı değil. "Genel dizi" sınıfının nasıl uygulanacağı konusunda rehberlik arayan kişiler, soru başlığını okuduklarında okumayı / durduracaklardır. Ve 30 cevaplı bir Q bulduğunda, sonuna kadar kaydırma ve bir SO yeni geleninden sıfır oy yanıtı okuma olasılığı düşüktür.
Stephen C

0

Vnportnoy'a göre sözdizimi

GenSet<Integer> intSet[] = new GenSet[3];

, doldurulması gereken bir dizi boş başvuru oluşturur

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

hangi tip güvenlidir.


-1
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}

Kodunuza her zaman bir açıklama eklemeli ve gönderilen orijinal soruyu neden çözdüğünü açıklamalısınız.
mjuarez

-1

Java'da genel dizi oluşturmaya izin verilmiyor, ancak bunu istediğiniz gibi yapabilirsiniz

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
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.