Sebuah gudang dapat menyimpan banyak jenis isi: rak material menyimpan string nama barang, sedangkan rak penomoran menyimpan int nomor rak. Pemakai kode kita sebaiknya cukup bergantung pada satu jaminan kemampuan yang sama, yaitu menambah item dan mengambil item pertama, tanpa peduli jenis isinya. Generic interface menyatakan jaminan tersebut sebagai kontrak yang menerima type parameter, sehingga satu deklarasi melayani semua tipe.
Mendefinisikan Generic Interface #
Deklarasinya sama seperti interface biasa, tetapi nama interface diikuti type parameter. Type parameter tersebut boleh dipakai pada signature member mana pun:
public interface IPenyimpanan<T>
{
void Tambah(T item);
T AmbilPertama();
}Interface ini tidak mengatur isi T; deklarasi hanya menjanjikan bahwa Tambah menerima nilai bertipe T dan AmbilPertama mengembalikan nilai bertipe T yang sama. Dengan pola itu, type parameter bekerja sebagai mata uang bersama antara member-member di dalam kontrak. Interface generik juga boleh menyertakan constraint pada type parameternya, misalnya where T : class, dan constraint itu menjadi syarat bagi setiap class implementator maupun pemakai kontrak.
Implementasi pada Generic Class #
Class implementator menentukan bentuk konkrit dari T. Gudang memilih untuk meneruskan type parameter miliknya sendiri, sehingga satu class Gudang<T> menepati kontrak untuk tipe apa pun:
public class Gudang<T> : IPenyimpanan<T>
{
private List<T> rak = new List<T>();
public void Tambah(T item)
{
rak.Add(item);
}
public T AmbilPertama()
{
return rak[0];
}
}
Gudang<string> gudangMaterial = new Gudang<string>();
gudangMaterial.Tambah("Semen 50 kg");
gudangMaterial.Tambah("Paku 1 kg");
Console.WriteLine(gudangMaterial.AmbilPertama());
Gudang<int> gudangNomorRak = new Gudang<int>();
gudangNomorRak.Tambah(101);
gudangNomorRak.Tambah(102);
Console.WriteLine(gudangNomorRak.AmbilPertama());
IPenyimpanan<string> penyimpanan = gudangMaterial;
Console.WriteLine(penyimpanan.AmbilPertama());Output yang Dihasilkan:
Semen 50 kg
101
Semen 50 kgMari kita bedah baris demi baris:
public class Gudang<T> : IPenyimpanan<T>: Type parameterGudangditeruskan ke kontrak, sehinggaGudang<string>mengimplementasikanIPenyimpanan<string>.private List<T> rak = ...: List generik menampung item bertipeT; elemen 0 selalu menjadi item pertama.void Tambah(T item)danT AmbilPertama(): Kedua member mengisi kontrakIPenyimpanan<T>persis seperti yang dijanjikan.gudangMaterial.Tambah("Semen 50 kg"): Compiler menolak argumen selain string, karena type argument sudah terkunci padaGudang<string>.IPenyimpanan<string> penyimpanan = gudangMaterial;: Variable bertipe interface menampung object implementator; pemanggilan berjalan lewat kontrak, tanpa tahu classGudang.
Pada contoh di atas, type parameter milik Gudang diteruskan langsung ke interface. Pola lain juga sah: class biasa tanpa type parameter boleh mengimplementasikan instantiasi konkret, misalnya class ArsipKwitansi : IPenyimpanan<string>, sehingga kontrak yang sama melayani class generik maupun class tertutup.
Satu class juga boleh mengimplementasikan beberapa instantiasi sekaligus, misalnya IPenyimpanan<string> dan IPenyimpanan<int>, karena keduanya dianggap kontrak yang berbeda.