Monday, December 26, 2016

Bab 13. Soal & Penyelesaian Java


13
Pemrograman Generik






Istilah generik berarti tipe terparameterisasi. Tipe terparameterisasi penting karena ia memampukan Anda untuk menciptakan kelas, antarmuka, dan metode dimana di dalamnya tipe data ditetapkan sebagai parameter. Dengan generik, adalah memungkinkan untuk menciptakan satu kelas yang secara otomatis dapat dipakai untuk pelbagai tipe.

Soal dan Penyelesaian
1.       Tulislah sebuah program yang mendefinisikan dua kelas. Yang pertama adalah kelas Gen, dan kelas kedua adalah DemoGen, yang menggunakan Gen.

Penyelesaian

// Sebuah kelas generik sederhana.
// Di sini, T merupakan parameter tipe yang
// akan digantikan oleh tipe riil
// ketika sebuah objek bertipe Gen diciptakan.
clabss Gen<T> {
    T ob; // mendeklarasikan sebuah objek bertipe T

    // Melewatkan sebuah referensi yang menunjuk ke objek bertipe T
    // kepada konstruktor
    Gen(T o) {
        ob = o;
    }

    // Menghasilkan ob.
    T getob() {
        return ob;
    }

    // Menampilkan tipe dari T.
    void tampilTipe() {
        System.out.println("Tipe dari T adalah " +
        ob.getClass().getName());
    }
}

// Mendemonstrasikan kelas generik.
public class DemoGen {
    public static void main(String args[]) {
        // Menciptakan sebuah referensi Gen untuk Integer.
        Gen<Integer> iOb;

        // Menciptakan sebuah objek Gen<Integer> dan menugaskan referensinya
        // kepada iOb. Perhatikan penggunaan autoboxing
        // untuk mengenkapsulasi nilai 88 di dalam sebuah objek Integer.
        iOb = new Gen<Integer>(88);

        // Menampilkan tipa data yang digunakan oleh iOb.
        iOb.tampilTipe();

        // Mendapatkan nilai di dalam iOb. Perhatikan bahwa
        // tidak perlu cast.
        int v = iOb.getob();
        System.out.println("nilai: " + v);
        System.out.println();

        // Menciptakan sebuah objek Gen untuk String.
        Gen<String> strOb = new Gen<String>("Test Generik");

        // Menampilkan tipa data yang digunakan oleh strOb.
        strOb.tampilTipe();

        // Mendapatkan nilai dari strOb. Sekali lagi, perhatikan bahwa
        // cast tidak diperlukan.
        String str = strOb.getob();
        System.out.println("nilai: " + str);
    }
}

Keluaran Program

Tipe dari T adalah java.lang.Integer
nilai: 88

Tipe dari T adalah java.lang.String
nilai: Test Generik

2.       Anda dapat mendeklarasikan lebih dari satu parameter tipe di dalam tipe generik. Untuk menetapkan dua atau lebih parameter tipe, Anda cukup menggunakan daftar (menggunakan koma sebagai pimsah). Tulislah sebuah program untuk mengilustrasikan situasi ini.

Penyelesaian

// Sebuah kelas generik sederhana dengan dua
// tipe parameter: T dan V.
class DuaGen<T, V> {
    T ob1;
    V ob2;

    // Melewatkan kepada konstruktor sebuah referensi yang menunjuk ke
    // sebuah objek bertipe T dan sebuah objek bertipe V.
    DuaGen(T o1, V o2) {
        ob1 = o1;
        ob2 = o2;
    }

    // Menampilkan tipe dari T dan V.
    void tampilTipe() {
        System.out.println("Tipe dari T adalah " +
            ob1.getClass().getName());
        System.out.println("Tipe dari V adalah " +
            ob2.getClass().getName());
    }

    T getob1() {
        return ob1;
    }

    V getob2() {
        return ob2;
    }
}

public class GenerikSederhana {
    public static void main(String args[]) {
        DuaGen<Integer, String> tgObj =
            new DuaGen<Integer, String>(88, "Generik");

        // Menampilkan tipe-tipe.
        tgObj.tampilTipe();

        // medapatkan dan menampilkan nilai-nilai.
        int v = tgObj.getob1();
        System.out.println("nilai: " + v);

        String str = tgObj.getob2();
        System.out.println("nilai: " + str);
    }
}

Keluaran Program

Tipe dari T adalah java.lang.Integer
Tipe dari V adalah java.lang.String
nilai: 88
nilai: Generik

3.       Pada dua soal sebelumnya, parameter tipe dapat digantikan oleh tipe kelas apapun. Ini baik untuk banyak kasus, tetapi kadangkala Anda perlu membatasi tipe-tipe apa saja yang dapat dilewatkan kepada sebuah parameter tipe. Dimisalkan bahwa Anda ingin menciptakan sebuah kelas generik yang memuat sebuah metode yang menghasilkan rerata dari sebuah array yang memuat angka-angka. Selanjutnya, Anda ingin menggunakan kelas untuk mendapatkan rerata tersebut untuk angka-angka dengan tipe-tipe data numerik, termasuk integer, float, dan double.

Untuk menangani situasi semacam ini, Java menyediakan klausa extends ketika penetapan parameter tipe dilakukan. Tulislah sebuah program untuk mengilustrasikan situasi ini.

Penyelesaian

// Pada program ini, argumen tipe untuk T
// haruslah Number, atau sebuah kelas yang diderivasi
// dari Number.
class Stats<T extends Number> {
    T[] arrayAngka; // array Number atau subkelas bertipe Number

    // Melewatkan kepada konstruktor sebuah referensi yang menunjuk ke
    // sebuah array atau subkelas bertipe Number.
    Stats(T[] o) {
        arrayAngka = o;
    }

    // Menghasilkan tipe double pada semua kelas.
    double rerata() {
        double jum = 0.0;
        for(int i=0; i < arrayAngka.length; i++)
            jum += arrayAngka[i].doubleValue();
        return jum / arrayAngka.length;
    }
}

public class DemoGenerikTerkekang {
    public static void main(String args[]) {
        Integer arrayInteger[] = { 1, 2, 3, 4, 5 };
        Stats<Integer> iob = new Stats<Integer>(arrayInteger);
       
        double v = iob.rerata();
        System.out.println("rerata iob average adalah " + v);
       
        Double arrayDouble[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
        Stats<Double> dob = new Stats<Double>(arrayDouble);
       
        double w = dob.rerata();
        System.out.println("rerata dob adalah " + w);
       
        // Ini tidak bisa dikompilasi karena String bukanlah
        // subkelas dari Number.
        // String arrayString[] = { "1", "2", "3", "4", "5" };
        // Stats<String> strob = new Stats<String>(arrayString);
        // double x = strob.average();
        // System.out.println("rerata strob adalah " + v);
    }
}

Keluaran Program

rerata iob average adalah 3.0
rerata dob adalah 3.3

4.       Diberikan kelas Stats seperti pada soal no 3 dan diasumsikan bahwa Anda ingin menambahkan sebuah metode bernama rerataSama yang menentukan apakah dua objek Stats memuat array-array dengan nilai reraya sama, tanpa memandang tipe dari data numeri pada tiap objek. Sebagai contoh, jika satu objek memuat nilai-nilai double 1.0, 2.0, dan 3.0, dan objek lain memuat nilai-nilai integer 2, 1, dan 3, maka reratanya akan sama. Tulislah sebuah program untuk mengilustrasikan situasi ini.

Penyelesaian

class Stats<T extends Number> {
    T[] arrayAngka; // array of Number or subclass
   
    // Melewatkan kepada konstruktor sebuah referensi yang menunjuk ke
    // sebuah array atau subkelas bertipe Number.
    Stats(T[] o) {
        arrayAngka = o;
    }

    // Menghasilkan tipe double pada semua kelas.
    double rerata() {
        double jum = 0.0;
        for(int i=0; i < arrayAngka.length; i++)
            jum += arrayAngka[i].doubleValue();
        return jum / arrayAngka.length;
    }
   
    // Menentukan jika dua rerata sama.
    // Perhatikan penggunaan wildcard (?).
    boolean rerataSama(Stats<?> ob) {
        if(rerata() == ob.rerata())
            return true;
        return false;
    }
}

public class DemoGenerik {
    public static void main(String args[]) {
        Integer arrayInteger[] = { 1, 2, 3, 4, 5 };
        Stats<Integer> iob = new Stats<Integer>(arrayInteger);

        double v = iob.rerata();
        System.out.println("rerata iob adalah " + v);

        Double arrayDouble[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
        Stats<Double> dob = new Stats<Double>(arrayDouble);

        double w = dob.rerata();
        System.out.println("rerata dob adalah " + w);

        Float arrayFloat[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
        Stats<Float> fob = new Stats<Float>(arrayFloat);

        double x = fob.rerata();
        System.out.println("rerata fob adalah " + x);

        // Melihat apakah array memiliki rerata sama.
        System.out.print("Rerata dari iob dan dob ");
        if(iob.rerataSama(dob))
            System.out.println("adalah sama.");
        else
            System.out.println("berbeda.");
       
        System.out.print("Rerata dari iob dan fob ");
        if(iob.rerataSama(fob))
            System.out.println("adalah sama.");
        else
            System.out.println("berbeda.");
    }
}

Keluaran Program

rerata iob adalah 3.0
rerata dob adalah 3.3
rerata fob adalah 3.0
Rerata dari iob dan dob berbeda.
Rerata dari iob dan fob adalah sama.

5.       Tulislah sebuah program lain untuk mendemonstrasikan pengekangan argumen, seperti pengekangan parameter.

Penyelesaian

// Argumen Wildcard terkekang.
// Koordinat dua-dimensi.
class DuaD {
    int x, y;

    DuaD(int a, int b) {
        x = a;
        y = b;
    }
}

// Koordinat tiga-dimensi.
class TigaD extends DuaD {
    int z;

    TigaD(int a, int b, int c) {
        super(a, b);
        z = c;
    }
}

// Koordinat empat-dimensi.
class EmpatD extends TigaD {
    int t;

    EmpatD(int a, int b, int c, int d) {
        super(a, b, c);
        t = d;
    }
}

// Kelas ini memuat sebuah array yang memuat objek-objek koordinat.
class Koordinat<T extends DuaD> {
    T[] arrayKoord;
    Koordinat(T[] o) { arrayKoord = o; }
}

// Mendemonstrasikan wildcar terkekang.
public class DemoPengekanganArgumen {
    static void tampilXY(Koordinat<?> c) {
        System.out.println("Koordinat X Y:");
        for(int i=0; i < c.arrayKoord.length; i++)
            System.out.println(c.arrayKoord[i].x + " " +
        c.arrayKoord[i].y);
       
        System.out.println();
    }
   
    static void tampilXYZ(Koordinat<? extends TigaD> c) {
        System.out.println("Koordinat X Y Z:");
        for(int i=0; i < c.arrayKoord.length; i++)
            System.out.println(c.arrayKoord[i].x + " " +
        c.arrayKoord[i].y + " " +
        c.arrayKoord[i].z);

        System.out.println();
    }

    static void tampilSemua(Koordinat<? extends EmpatD> c) {
        System.out.println("Koordinat X Y Z T:");
        for(int i=0; i < c.arrayKoord.length; i++)
            System.out.println(c.arrayKoord[i].x + " " +
                c.arrayKoord[i].y + " " +
                c.arrayKoord[i].z + " " +
                c.arrayKoord[i].t);
        System.out.println();
    }

    public static void main(String args[]) {
        DuaD td[] = {
            new DuaD(0, 0),
            new DuaD(7, 9),
            new DuaD(18, 4),
            new DuaD(-1, -23)
        };

        Koordinat<DuaD> lokasi = new Koordinat<DuaD>(td);
        System.out.println("Isi dari lokasi.");
        tampilXY(lokasi); // OK, merupakan objek DuaD
        // tampilXYZ(lokasi); // Error, bukan objek TigaD
        // tampilSemua(lokasi); // Error, bukan objek EmpatD

        // Sekarang, menciptakan empat objek EmpatD.
        EmpatD fd[] = {
            new EmpatD(1, 2, 3, 4),
            new EmpatD(6, 8, 14, 8),
            new EmpatD(22, 9, 4, 9),
            new EmpatD(3, -2, -23, 17)
        };

        Koordinat<EmpatD> lokasi2 = new Koordinat<EmpatD>(fd);
        System.out.println("Isi dari lokasi2.");

        // Semua ini OK.
        tampilXY(lokasi2);
        tampilXYZ(lokasi2);
        tampilSemua(lokasi2);
    }
}

Keluaran Program

Isi dari lokasi.
Koordinat X Y:
0 0
7 9
18 4
-1 -23

Isi dari lokasi2.
Koordinat X Y:
1 2
6 8
22 9
3 -2

Koordinat X Y Z:
1 2 3
6 8 14
22 9 4
3 -2 -23

Koordinat X Y Z T:
1 2 3 4
6 8 14 8
22 9 4 9
3 -2 -23 17

6.       Tulislah sebuah program yang menggunakan metode generik.

Penyelesaian

// Demonstrasi sebuah metode generik sederhana.
public class DemoMetodeGenerik {
    // Menentukan apakah sebuah objek ada di dalam suatu array.
    static <T, V extends T> boolean ApaDiDalam(T x, V[] y) {
        for(int i=0; i < y.length; i++)
            if(x.equals(y[i])) return true;
        return false;
    }

    public static void main(String args[]) {
        // Menggunakan ApaDiDalam() pada Integer.
        Integer arrayAngka[] = { 1, 2, 3, 4, 5 };

        if(ApaDiDalam(2, arrayAngka))
            System.out.println("2 ada di dalam arrayAngka");

        if(!ApaDiDalam(7, arrayAngka))
            System.out.println("7 ada di dalam arrayAngka");
        System.out.println();

        // Menggunakan ApaDiDalam() pada String.
        String arrayString[] = { "satu", "dua", "tiga",
                                 "empat", "lima" };

        if(ApaDiDalam("dua", arrayString))
            System.out.println("dua adalah di dalam arrayString");

        if(!ApaDiDalam("tujuh", arrayString))
            System.out.println("tujuh tidak ada di dalam arrayString");

        // Tidak bisa dikompilasi! Tipe-tipe harus kompatibel.
        // if(arrayString("dua arrayAngka))
        // System.out.println("dua adalah di dalam arrayString");
    }
}

Keluaran Program

2 ada di dalam arrayAngka
7 ada di dalam arrayAngka

dua adalah di dalam arrayString
tujuh tidak ada di dalam arrayString

7.       Adalah memungkinkan untuk konstruktor menjadi generik, meskipun kelasnya tidak generik. Tulislah sebuah program yang mengilustrasikan situasi ini.

Penyelesaian

// Menggunakan sebuah konstruktor generik.
class KonstruktorGenerik {
    private double nil;

    <T extends Number> KonstruktorGenerik(T arg) {
        nil = arg.doubleValue();
    }

    void tampilNil() {
        System.out.println("nilai: " + nil);
    }
}

public class DemoKonstruktorGenerik {
    public static void main(String args[]) {
        KonstruktorGenerik test = new KonstruktorGenerik(100);
        KonstruktorGenerik test2 = new KonstruktorGenerik(123.5F);

        test.tampilNil();
        test2.tampilNil();
    }
}

Keluaran Program

nilai: 100.0
nilai: 123.5

8.       Selain kelas dan metode generik, Anda juga dapat memiliki antarmuka generik. Antarmuka generik ditetapkan sama seperti kelas generik. Tulislah sebuah program yang mengilustrasikan situasi ini.

Penyelesaian

// Antarmuka MinMaks.
interface MinMaks<T extends Comparable<T>> {
    T min();
    T maks();
}
// Sekarang, mengimplementasikan MinMaks
class KelasKu<T extends Comparable<T>> implements MinMaks<T> {
    T[] arrayNilai;
    KelasKu(T[] o) { arrayNilai = o; }

    // Menghasilkan nilai minimum di dalam arrayNilai.
    public T min() {
        T v = arrayNilai[0];
        for(int i=1; i < arrayNilai.length; i++)
            if(arrayNilai[i].compareTo(v) < 0) v = arrayNilai[i];
        return v;
    }

    // Menghasilkan nilai maksimum di dalam arrayNilai.
    public T maks() {
        T v = arrayNilai[0];
        for(int i=1; i < arrayNilai.length; i++)
            if(arrayNilai[i].compareTo(v) > 0) v = arrayNilai[i];
        return v;
    }
}

public class DemoAntarmukaGenerik {
    public static void main(String args[]) {
        Integer arrayInteger[] = {3, 6, 2, 8, 6 };
        Character arrayKarakter[] = {'b', 'r', 'p', 'w' };

        KelasKu<Integer> iob = new KelasKu<Integer>(arrayInteger);
        KelasKu<Character> cob = new KelasKu<Character>(arrayKarakter);

        System.out.println("Nilai maks di dalam arrayInteger: " + iob.maks());
        System.out.println("Nilai min di dalam arrayInteger: " + iob.min());
        System.out.println("Nilai maks di dalam arrayKarakter: " + cob.maks());
        System.out.println("Nilai min di dalam arrayKarakter: " + cob.min());
    }
}

Keluaran Program

Nilai maks di dalam arrayInteger: 8
Nilai min di dalam arrayInteger: 2
Nilai maks di dalam arrayKarakter: w
Nilai min di dalam arrayKarakter: b

9.       Kelas-kelas generik dapat menjadi bagian dari hirarki pewarisan kelas sama seperti kelas tak-generik. Jadi, sebuah kelas generik dapat berperan sebagai superkelas atau sebagai subkelas. Perbedaan kunci antara hirarki generik dan hirarki tak-generik adalah bahwa di dalam hirarki generik, setiap argumen tipe yang diperlukan oleh superkelas generik harus dilewatkan ke atas hirarki oleh semua subkelas. Ini sama dengan ketika argumen-argumen konstruktor dilewatkan ke atas hirarki. Tulislah sebuah program yang mengilustrasikan situasi ini.

Penyelesaian

// Sebuah subkelas dapat menambahkan parameter tipenya sendiri.
class Gen<T> {
    T ob; // mendeklarasikan sebuah objek bertipe T

    // Melewatkan kepada konstruktor sebuah referensi yang menunjuk ke
    // sebuah objek bertipe T.
    Gen(T o) {
        ob = o;
    }

    // Menghasilkan ob.
    T getob() {
        return ob;
    }
}

// Sebuah subkelas dari Gen yang mendefinisikan
// parameter tipe kedua, dinamakan V.
class Gen2<T, V> extends Gen<T> {
    V ob2;

    Gen2(T o, V o2) {
        super(o);
        ob2 = o2;
    }

    V getob2() {
        return ob2;
    }
}

// Menciptakan sebuah objek bertipe Gen2.
public class DemoPewarisanGenerik {
    public static void main(String args[]) {
        // Menciptakan sebuah objek Gen2 untuk String dan Integer.
        Gen2<String, Integer> x =
            new Gen2<String, Integer>("Nilai adalah: ", 99);

        System.out.print(x.getob());
        System.out.println(x.getob2());
    }
}

Keluaran Program

Nilai adalah: 99

10.    Adalah hal yang memungkinkan bagi kelas tak-generik menjadi superkelas bagi subkelas generik. Tulislah sebuah program yang mengilustrasikan situasi ini.

Penyelesaian

// Sebuah kelas tak-generik dapat menjadi superkelas
// bagi subkelas generik.

// Sebuah kelas tak-generik.
class TakGen {
    int angka;

    TakGen(int i) {
        angka = i;
    }

    int getAngka() {
        return angka;
    }
}

// Sebuah subkelas generik.
class Gen<T> extends TakGen {
    T ob; // mendeklarasikan sebuah objek bertipe T

    // Melewatkan konstruktor sebuah referensi yang menunjuk ke
    // sebuah objek bertipe T.
    Gen(T o, int i) {
        super(i);
        ob = o;
    }

    // Menghasilkan ob.
    T getob() {
        return ob;
    }
}

// Menciptakan sebuah objek Gen.
public class DemoPewarisanGenerik2 {
    public static void main(String args[]) {
        // Menciptakan sebuah objek Gen bertipe String.
        Gen<String> w = new Gen<String>("Selamat Malam", 47);

        System.out.print(w.getob() + " ");
        System.out.println(w.getAngka());
    }
}

Keluaran Program

Selamat Malam 47

11.    Sebuah metode generik di dalam suatu kelas generik dapat didefinisikan-ulang sama seperti metode lainnya. Tulislah sebuah program yang mengilustrasikan situasi ini.

Penyelesaian

// Mendefinisikan-ulang sebuah metode generik di dalam kelas generik.
class Gen<T> {
    T ob; // mendeklarasikan sebuah objek bertipe T

    // Melewatkan kepada konstruktor sebuah referensi yang menunjuk ke
    // sebuah objek bertipe T.
    Gen(T o) {
        ob = o;
    }

    // menghasilkan ob.
    T getob() {
        System.out.print("metode getob() dari kelas Gen: " );
        return ob;
    }
}

// Sebuah subkelas dari Gen yang mendefinisikan-ulang getob().
class Gen2<T> extends Gen<T> {
    Gen2(T o) {
        super(o);
    }

    // Mendefinisikan-ulang getob().
    T getob() {
        System.out.print("metode getob() dan kelas Gen2: ");
        return ob;
    }
}

// Mendemonstrasikan pendefinisian-ulang metode generik.
public class DemoDefinisiUlangMetodeGenerik {
    public static void main(String args[]) {
        // Menciptakan sebuah objek Gen untuk Integer.
        Gen<Integer> iOb = new Gen<Integer>(88);

        // Menciptakan sebuah objek Gen2 untuk Integer.
        Gen2<Integer> iOb2 = new Gen2<Integer>(99);

        // Menciptakan sebuah objek Gen2 untuk String.
        Gen2<String> strOb2 = new Gen2<String>("Test Generik");

        System.out.println(iOb.getob());
        System.out.println(iOb2.getob());
        System.out.println(strOb2.getob());
    }
}

Keluaran Program

metode getob() dari kelas Gen: 88
metode getob() dan kelas Gen2: 99
metode getob() dan kelas Gen2: Test Generik




No comments:

Post a Comment