JavaRush /Blog Jawa /Random-JV /Ngopi #130. Cara nggarap array Jawa kanthi bener - tips s...

Ngopi #130. Cara nggarap array Jawa kanthi bener - tips saka Oracle

Diterbitake ing grup
Sumber: Oracle Working with arrays bisa kalebu ekspresi refleksi, generik, lan lambda. Aku iki bubar ngomong karo kolega sing develops ing C. Obrolan nguripake kanggo arrays lan carane padha bisa ing Jawa dibandhingake C. Aku ketemu iki sethitik aneh, amarga Jawa dianggep minangka basa C-kaya. Padha bener duwe akeh podho, nanging ana uga beda. Ayo dadi miwiti prasaja. Ngopi #130.  Cara nggarap array Jawa kanthi bener - tips saka Oracle - 1

Deklarasi Array

Yen sampeyan tindakake tutorial Jawa, sampeyan bakal weruh sing ana rong cara kanggo wara-wara array. Sing pisanan langsung:
int[] array; // a Java array declaration
Sampeyan bisa ndeleng kepiye bedane karo C, ing endi sintaks kasebut:
int array[]; // a C array declaration
Ayo bali menyang Jawa maneh. Sawise ngumumake array, sampeyan kudu ngalokasiake:
array = new int[10]; // Java array allocation
Apa bisa ngumumake lan miwiti array bebarengan? Bener ora:
int[10] array; // NOPE, ERROR!
Nanging, sampeyan bisa ngumumake lan miwiti array langsung yen sampeyan wis ngerti nilai kasebut:
int[] array = { 0, 1, 1, 2, 3, 5, 8 };
Apa yen sampeyan ora ngerti artine? Iki kode sing paling kerep sampeyan deleng kanggo ngumumake, ngalokasi, lan nggunakake array int :
int[] array;
array = new int[10];
array[0] = 0;
array[1] = 1;
array[2] = 1;
array[3] = 2;
array[4] = 3;
array[5] = 5;
array[6] = 8;
...
Elinga yen aku nemtokake array int , yaiku array saka jinis data primitif Jawa . Ayo ndeleng apa sing kedadeyan yen sampeyan nyoba proses sing padha karo macem-macem obyek Jawa tinimbang primitif:
class SomeClass {
    int val;
    // …
}
SomeClass[] array = new SomeClass[10];
array[0].val = 0;
array[1].val = 1;
array[2].val = 1;
array[3].val = 2;
array[4].val = 3;
array[5].val = 5;
array[6].val = 8;
Yen kita mbukak kode ing ndhuwur, kita bakal entuk pangecualian sanalika sawise nyoba nggunakake unsur pisanan saka array. Kenging punapa? Sanajan array dialokasikan, saben bagean saka array ngemot referensi obyek kosong. Yen sampeyan ngetik kode iki menyang IDE, bakal malah otomatis isi ing .val kanggo sampeyan, supaya kesalahan bisa bingung. Kanggo ndandani bug, tindakake langkah iki:
SomeClass[] array = new SomeClass[10];
for ( int i = 0; i < array.length; i++ ) {  //new code
    array[i] = new SomeClass();             //new code
}                                           //new code
array[0].val = 0;
array[1].val = 1;
array[2].val = 1;
array[3].val = 2;
array[4].val = 3;
array[5].val = 5;
array[6].val = 8;
Nanging ora elegan. Aku kepingin weruh kok aku ora bisa gampang nyedhiakke Uploaded lan obyek ing Uploaded karo kode kurang, Mungkin malah kabeh ing siji baris. Kanggo nemokake jawaban, aku nindakake sawetara eksperimen.

Nemokake nirwana ing antarane susunan Jawa

Tujuan kita yaiku kode kanthi elegan. Sawise aturan "kode resik", aku mutusake nggawe kode sing bisa digunakake maneh kanggo ngresiki pola alokasi array. Iki nyoba pisanan:
public class MyArray {

    public static Object[] toArray(Class cls, int size)
      throws Exception {
        Constructor ctor = cls.getConstructors()[0];
        Object[] objects = new Object[size];
        for ( int i = 0; i < size; i++ ) {
            objects[i] = ctor.newInstance();
        }

        return objects;
    }

    public static void main(String[] args) throws Exception {
        SomeClass[] array1 = (SomeClass[])MyArray.toArray(SomeClass.class, 32); // see this
        System.out.println(array1);
    }
}
Baris kode sing ditandhani "ndeleng iki" katon persis kaya sing dikarepake, amarga implementasine toArray . Pendekatan iki nggunakake refleksi kanggo nemokake konstruktor standar kanggo kelas sing diwenehake lan banjur nelpon konstruktor kasebut kanggo instantiate obyek saka kelas kasebut. Proses kasebut nelpon konstruktor sapisan kanggo saben unsur array. Apik banget! Sayange yen ora bisa. Kode compiles nggoleki, nanging mbalang kesalahan ClassCastException nalika mbukak. Kanggo nggunakake kode iki, sampeyan kudu nggawe array saka unsur Obyek, lan banjur ngirim saben unsur array menyang kelas SomeClass kaya iki:
Object[] objects = MyArray.toArray(SomeClass.class, 32);
SomeClass scObj = (SomeClass)objects[0];
...
Iki ora elegan! Sawise eksperimen luwih akeh, aku ngembangake sawetara solusi nggunakake ekspresi refleksi, generik, lan lambda.

Solusi 1: Gunakake refleksi

Kene kita nggunakake java.lang.reflect.Array kelas kanggo instantiate Uploaded saka kelas sampeyan nemtokake tinimbang nggunakake basa java.lang.Object kelas . Iki minangka owah-owahan kode siji baris:
public static Object[] toArray(Class cls, int size) throws Exception {
    Constructor ctor = cls.getConstructors()[0];
    Object array = Array.newInstance(cls, size);  // new code
    for ( int i = 0; i < size; i++ ) {
        Array.set(array, i, ctor.newInstance());  // new code
    }
    return (Object[])array;
}
Sampeyan bisa nggunakake pendekatan iki kanggo njaluk array saka kelas sing dikarepake lan banjur bisa karo kaya iki:
SomeClass[] array1 = (SomeClass[])MyArray.toArray(SomeClass.class, 32);
Senajan iki ora owah-owahan dibutuhake, baris kapindho wis diganti nggunakake kelas bayangan Array kanggo nyetel isi saben unsur Uploaded. Iki apik tenan! Nanging ana siji liyane rinci sing ora koyone cukup tengen: cast kanggo SomeClass [] ora katon apik banget. Untunge, ana solusi karo generik.

Solusi 2: Gunakake generik

Framework Koleksi nggunakake generik kanggo jinis naleni lan ngilangake cast kanggo wong-wong mau ing akeh operasi sawijining. Generik uga bisa digunakake ing kene. Ayo dadi contone java.util.List .
List list = new ArrayList();
list.add( new SomeClass() );
SomeClass sc = list.get(0); // Error, needs a cast unless...
Baris katelu ing potongan ndhuwur bakal nggawe kesalahan kajaba sampeyan nganyari baris pisanan kaya iki:
List<SomeClass> = new ArrayList();
Sampeyan bisa entuk asil sing padha kanthi nggunakake generik ing kelas MyArray . Punika versi anyar:
public class MyArray<E> {
    public <E> E[] toArray(Class cls, int size) throws Exception {
        E[] array = (E[])Array.newInstance(cls, size);
        Constructor ctor = cls.getConstructors()[0];
        for ( int element = 0; element < array.length; element++ ) {
            Array.set(array, element, ctor.newInstance());
        }
        return arrayOfGenericType;
    }
}
// ...
MyArray<SomeClass> a1 = new MyArray(SomeClass.class, 32);
SomeClass[] array1 = a1.toArray();
Iku katon apik. Kanthi nggunakake generik lan kalebu jinis target ing deklarasi, jinis kasebut bisa disimpulake ing operasi liyane. Kajaba iku, kode iki bisa dikurangi dadi siji baris kanthi nindakake iki:
SomeClass[] array = new MyArray<SomeClass>(SomeClass.class, 32).toArray();
Misi wis rampung, bener? Inggih, ora cukup. Iki apik yen sampeyan ora peduli sing konstruktor kelas sampeyan nelpon, nanging yen sampeyan pengin nelpon konstruktor tartamtu, banjur solusi iki ora bisa. Sampeyan bisa terus nggunakake refleksi kanggo ngatasi masalah iki, nanging kode kasebut bakal dadi kompleks. Untunge, ana ekspresi lambda sing menehi solusi liyane.

Solusi 3: Gunakake ekspresi lambda

Aku bakal ngakoni, aku ora seneng banget babagan ekspresi lambda sadurunge, nanging aku wis sinau kanggo ngapresiasi. Utamane, aku seneng antarmuka java.util.stream.Stream , sing nangani koleksi obyek. Stream mbantu aku tekan nirwana array Jawa. Iki minangka upaya pertamaku nggunakake lambdas:
SomeClass[] array =
    Stream.generate(() -> new SomeClass())
    .toArray(SomeClass[]::new);
Aku wis bejat kode iki dadi telung baris kanggo maca luwih gampang. Sampeyan bisa ndeleng sing ticks kabeh kothak: iku prasaja lan elegan, nggawe Uploaded populated saka kedadean obyek, lan ngijini sampeyan kanggo nelpon konstruktor tartamtu. Pay manungsa waé menyang toArray method parameter : SomeClass[]::new . Iki minangka fungsi generator sing digunakake kanggo nyedhiyakake array saka jinis sing ditemtokake. Nanging, kaya saiki, kode iki duwe masalah cilik: nggawe macem-macem ukuran tanpa wates. Iki ora optimal banget. Nanging masalah bisa ditanggulangi kanthi nelpon metode watesan :
SomeClass[] array =
    Stream.generate(() -> new SomeClass())
    .limit(32)   // calling the limit method
    .toArray(SomeClass[]::new);
Array saiki diwatesi dadi 32 unsur. Sampeyan bisa uga nyetel nilai obyek tartamtu kanggo saben unsur array, kaya sing kapacak ing ngisor iki:
SomeClass[] array = Stream.generate(() -> {
    SomeClass result = new SomeClass();
    result.val = 16;
    return result;
    })
    .limit(32)
    .toArray(SomeClass[]::new);
Kode iki nuduhake kekuwatan ekspresi lambda, nanging kode kasebut ora rapi utawa kompak. Ing mratelakake panemume, nelpon konstruktor liyane kanggo nyetel Nilai bakal luwih apik.
SomeClass[] array6 = Stream.generate( () -> new SomeClass(16) )
    .limit(32)
    .toArray(SomeClass[]::new);
Aku seneng solusi adhedhasar ekspresi lambda. Iku becik yen sampeyan kudu nelpon konstruktor tartamtu utawa bisa karo saben unsur Uploaded. Nalika aku butuh sing luwih gampang, aku biasane nggunakake solusi adhedhasar generik amarga luwih gampang. Nanging, sampeyan bisa ndeleng dhewe yen ekspresi lambda nyedhiyakake solusi sing elegan lan fleksibel.

Kesimpulan

Dina iki kita sinau babagan carane ngumumaké lan ngalokasi array primitif, ngalokasikan array elemen Obyek , nggunakake ekspresi refleksi, generik, lan lambda ing Jawa.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION