Saya pikir saya akan menawarkan softball ini kepada siapa pun yang ingin memukulnya keluar dari taman. Apa itu obat generik, apa kelebihan obat generik, mengapa, dimana, bagaimana cara menggunakannya? Harap pertahankan agar cukup mendasar. Terima kasih.
87
Jawaban:
sumber
Saya benar-benar benci untuk mengulang. Saya benci mengetik hal yang sama lebih sering daripada yang seharusnya. Saya tidak suka mengulang beberapa kali dengan sedikit perbedaan.
Daripada membuat:
class MyObjectList { MyObject get(int index) {...} } class MyOtherObjectList { MyOtherObject get(int index) {...} } class AnotherObjectList { AnotherObject get(int index) {...} }
Saya dapat membuat satu kelas yang dapat digunakan kembali ... (dalam kasus di mana Anda tidak ingin menggunakan koleksi mentah karena suatu alasan)
class MyList<T> { T get(int index) { ... } }
Saya sekarang 3x lebih efisien dan saya hanya perlu menyimpan satu salinan. Mengapa Anda TIDAK ingin mempertahankan lebih sedikit kode?
Ini juga berlaku untuk kelas non-koleksi seperti a
Callable<T>
atauReference<T>
yang harus berinteraksi dengan kelas lain. Apakah Anda benar-benar ingin memperluasCallable<T>
danFuture<T>
dan setiap kelas terkait lainnya untuk membuat versi yang aman bagi tipe?Bukan saya.
sumber
Tidak perlu typecast adalah salah satu keuntungan terbesar dari generik Java , karena ia akan melakukan pemeriksaan jenis pada waktu kompilasi. Ini akan mengurangi kemungkinan
ClassCastException
yang dapat dilemparkan saat runtime, dan dapat menghasilkan kode yang lebih kuat.Tetapi saya curiga Anda sepenuhnya menyadarinya.
Pada awalnya, saya juga tidak melihat manfaat obat generik. Saya mulai belajar Java dari sintaks 1.4 (meskipun Java 5 keluar pada saat itu) dan ketika saya menemukan generik, saya merasa lebih banyak kode untuk ditulis, dan saya benar-benar tidak mengerti manfaatnya.
IDE modern membuat penulisan kode dengan generik lebih mudah.
IDE yang paling modern dan layak cukup pintar untuk membantu menulis kode dengan obat generik, terutama dengan penyelesaian kode.
Berikut contoh pembuatan
Map<String, Integer>
dengan aHashMap
. Kode yang harus saya ketik adalah:Map<String, Integer> m = new HashMap<String, Integer>();
Dan memang, banyak sekali mengetik hanya untuk membuat yang baru
HashMap
. Namun, pada kenyataannya, saya hanya perlu mengetik sebanyak ini sebelum Eclipse tahu apa yang saya butuhkan:Map<String, Integer> m = new Ha
Ctrl+SpaceBenar, saya memang perlu memilih
HashMap
dari daftar kandidat, tetapi pada dasarnya IDE tahu apa yang harus ditambahkan, termasuk tipe generik. Dengan alat yang tepat, menggunakan obat generik tidak terlalu buruk.Selain itu, karena jenisnya diketahui, saat mengambil elemen dari koleksi generik, IDE akan bertindak seolah-olah objek tersebut sudah menjadi objek dari jenis yang dideklarasikan - tidak perlu melakukan casting agar IDE mengetahui jenis objek tersebut. adalah.
Keuntungan utama dari obat generik berasal dari cara kerjanya yang baik dengan fitur Java 5 yang baru. Berikut adalah contoh melempar bilangan bulat ke a
Set
dan menghitung totalnya:Set<Integer> set = new HashSet<Integer>(); set.add(10); set.add(42); int total = 0; for (int i : set) { total += i; }
Dalam potongan kode itu, ada tiga fitur Java 5 baru yang ada:
Pertama, generik dan autoboxing primitif memungkinkan baris berikut:
set.add(10); set.add(42);
Integer di
10
-autobox menjadi sebuahInteger
dengan nilai10
. (Dan sama untuk42
). Kemudian yangInteger
dilemparkan ke dalamSet
yang dikenal denganInteger
s. Mencoba memasukkanString
akan menyebabkan kesalahan kompilasi.Selanjutnya, for-each loop mengambil ketiga dari itu:
for (int i : set) { total += i; }
Pertama, s yang
Set
berisiInteger
digunakan dalam for-each loop. Setiap elemen dideklarasikan sebagai sebuahint
dan yang diizinkan saatInteger
dikembalikan ke kotak primitifint
. Dan fakta bahwa unboxing ini terjadi diketahui karena obat generik digunakan untuk menentukan bahwa ada yangInteger
disimpan di fileSet
.Generik bisa menjadi perekat yang menyatukan fitur-fitur baru yang diperkenalkan di Java 5, dan itu membuat pengkodean lebih sederhana dan lebih aman. Dan sebagian besar waktu IDE cukup pintar untuk membantu Anda dengan saran yang bagus, jadi umumnya, itu tidak akan lebih banyak mengetik.
Dan terus terang, seperti yang bisa dilihat dari
Set
contoh, saya merasa bahwa memanfaatkan fitur Java 5 dapat membuat kode lebih ringkas dan kuat.Edit - Contoh tanpa obat generik
Berikut adalah ilustrasi
Set
contoh di atas tanpa menggunakan obat generik. Itu mungkin, tetapi tidak terlalu menyenangkan:Set set = new HashSet(); set.add(10); set.add(42); int total = 0; for (Object o : set) { total += (Integer)o; }
(Catatan: Kode di atas akan menghasilkan peringatan konversi yang tidak dicentang pada waktu kompilasi.)
Saat menggunakan koleksi non-generik, tipe yang dimasukkan ke dalam koleksi adalah tipe objek
Object
. Oleh karena itu, dalam contoh ini, aObject
adalah apa yang sedangadd
diedit ke dalam himpunan.set.add(10); set.add(42);
Di baris di atas, autoboxing dalam bermain - primitif
int
nilai10
dan42
sedang autoboxed menjadiInteger
objek, yang sedang ditambahkan keSet
. Namun, perlu diingat,Integer
objek ditangani sebagaiObject
s, karena tidak ada informasi tipe untuk membantu kompilator mengetahui tipe apa yangSet
diharapkan.for (Object o : set) {
Ini adalah bagian yang sangat penting. Alasan for-each loop berfungsi adalah karena antarmuka
Set
mengimplementasikanIterable
, yang mengembalikanIterator
informasi with type, jika ada. (Iterator<T>
, itu.)Namun, karena tidak ada informasi tipe,
Set
kehendak mengembalikanIterator
yang akan mengembalikan nilai dalamSet
asObject
s, dan itulah mengapa elemen yang diambil di untuk-setiap loop harus berjenisObject
.Sekarang setelah
Object
diambil dariSet
, itu perlu dilemparkan keInteger
manual untuk melakukan penambahan:Di sini, typecast dilakukan dari sebuah
Object
keInteger
. Dalam hal ini, kami tahu ini akan selalu berfungsi, tetapi typecasting manual selalu membuat saya merasa ini adalah kode yang rapuh yang dapat rusak jika perubahan kecil dilakukan di tempat lain. (Saya merasa bahwa setiap typecastClassCastException
menunggu untuk terjadi, tetapi saya ngelantur ...)The
Integer
kini unboxed menjadiint
dan memungkinkan untuk melakukan penambahan ke dalamint
variabeltotal
.Saya harap saya dapat mengilustrasikan bahwa fitur baru Java 5 dapat digunakan dengan kode non-generik, tetapi tidak sebersih dan langsung menulis kode dengan obat generik. Dan, menurut pendapat saya, untuk memanfaatkan sepenuhnya fitur-fitur baru di Java 5, kita harus melihat ke generik, jika paling tidak, memungkinkan pemeriksaan waktu kompilasi untuk mencegah typecast yang tidak valid membuang pengecualian saat runtime.
sumber
Jika Anda mencari database bug Java sebelum 1.5 dirilis, Anda akan menemukan tujuh kali lebih banyak bug dengan
NullPointerException
thanClassCastException
. Jadi sepertinya bukan fitur yang bagus untuk menemukan bug, atau setidaknya bug yang tetap ada setelah sedikit pengujian asap.Bagi saya, keuntungan besar dari obat generik adalah bahwa mereka mendokumentasikan informasi jenis kode yang penting. Jika saya tidak ingin informasi jenis itu didokumentasikan dalam kode, maka saya akan menggunakan bahasa yang diketik secara dinamis, atau setidaknya bahasa dengan jenis inferensi yang lebih implisit.
Menyimpan koleksi objek untuk dirinya sendiri bukanlah gaya yang buruk (tetapi gaya yang umum adalah mengabaikan enkapsulasi secara efektif). Ini lebih tergantung pada apa yang Anda lakukan. Meneruskan koleksi ke "algoritme" sedikit lebih mudah untuk diperiksa (pada atau sebelum waktu kompilasi) dengan obat generik.
sumber
Generik di Java memfasilitasi polimorfisme parametrik . Melalui parameter tipe, Anda bisa meneruskan argumen ke tipe. Sama seperti metode seperti
String foo(String s)
memodelkan beberapa perilaku, tidak hanya untuk string tertentu, tetapi untuk string apa puns
, jadi tipe sepertiList<T>
memodelkan beberapa perilaku, tidak hanya untuk jenis tertentu, tetapi untuk jenis apa pun .List<T>
mengatakan bahwa untuk semua tipeT
, ada tipeList
yang elemennya adalahT
s . JadiList
sebenarnya adalah tipe konstruktor . Ini mengambil tipe sebagai argumen dan membangun tipe lain sebagai hasilnya.Berikut adalah beberapa contoh tipe generik yang saya gunakan setiap hari. Pertama, antarmuka umum yang sangat berguna:
public interface F<A, B> { public B f(A a); }
Antarmuka ini mengatakan bahwa untuk dua jenis,
A
danB
, ada fungsi (disebutf
) yang mengambilA
dan mengembalikan aB
. Saat Anda mengimplementasikan antarmuka ini,A
danB
bisa jenis apa pun yang Anda inginkan, selama Anda menyediakan fungsif
yang mengambil yang pertama dan mengembalikan yang terakhir. Berikut contoh implementasi antarmuka:F<Integer, String> intToString = new F<Integer, String>() { public String f(int i) { return String.valueOf(i); } }
Sebelum generik, polimorfisme dicapai dengan subclassing menggunakan
extends
kata kunci. Dengan obat generik, kita sebenarnya bisa menghilangkan subclass dan menggunakan polimorfisme parametrik sebagai gantinya. Misalnya, pertimbangkan kelas berparameterisasi (generik) yang digunakan untuk menghitung kode hash untuk semua jenis. Alih-alih menimpa Object.hashCode (), kita akan menggunakan kelas generik seperti ini:public final class Hash<A> { private final F<A, Integer> hashFunction; public Hash(final F<A, Integer> f) { this.hashFunction = f; } public int hash(A a) { return hashFunction.f(a); } }
Ini jauh lebih fleksibel daripada menggunakan pewarisan, karena kita bisa tetap menggunakan tema penggunaan komposisi dan polimorfisme parametrik tanpa mengunci hierarki yang rapuh.
Generik Java tidak sempurna. Anda bisa mengabstraksi tipe, tetapi Anda tidak bisa mengabstraksi konstruktor tipe, misalnya. Artinya, Anda dapat mengatakan "untuk semua tipe T", tetapi Anda tidak dapat mengatakan "untuk tipe T apa pun yang menggunakan parameter tipe A".
Saya menulis artikel tentang batasan generik Java ini, di sini.
Satu keuntungan besar dengan obat generik adalah mereka memungkinkan Anda menghindari subclass. Subclassing cenderung menghasilkan hierarki kelas yang rapuh yang canggung untuk diperluas, dan kelas yang sulit dipahami secara individual tanpa melihat seluruh hierarki.
Wereas sebelum generik Anda mungkin memiliki kelas seperti
Widget
diperpanjang olehFooWidget
,BarWidget
, danBazWidget
, dengan obat generik Anda bisa memiliki kelas generik tunggalWidget<A>
yang membutuhkanFoo
,Bar
atauBaz
dalam konstruktor untuk memberikanWidget<Foo>
,Widget<Bar>
danWidget<Baz>
.sumber
Generik menghindari pukulan kinerja tinju dan unboxing. Pada dasarnya, lihat ArrayList vs List <T>. Keduanya melakukan hal inti yang sama, tetapi List <T> akan jauh lebih cepat karena Anda tidak perlu melakukan box ke / dari objek.
sumber
Manfaat terbaik untuk Generics adalah penggunaan kembali kode. Katakanlah Anda memiliki banyak objek bisnis, dan Anda akan menulis kode yang SANGAT mirip untuk setiap entitas untuk melakukan tindakan yang sama. (IE Linq ke operasi SQL).
Dengan generik, Anda dapat membuat kelas yang akan dapat beroperasi dengan jenis apa pun yang mewarisi dari kelas dasar tertentu atau mengimplementasikan antarmuka yang diberikan seperti:
public interface IEntity { } public class Employee : IEntity { public string FirstName { get; set; } public string LastName { get; set; } public int EmployeeID { get; set; } } public class Company : IEntity { public string Name { get; set; } public string TaxID { get; set } } public class DataService<ENTITY, DATACONTEXT> where ENTITY : class, IEntity, new() where DATACONTEXT : DataContext, new() { public void Create(List<ENTITY> entities) { using (DATACONTEXT db = new DATACONTEXT()) { Table<ENTITY> table = db.GetTable<ENTITY>(); foreach (ENTITY entity in entities) table.InsertOnSubmit (entity); db.SubmitChanges(); } } } public class MyTest { public void DoSomething() { var dataService = new DataService<Employee, MyDataContext>(); dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 }); var otherDataService = new DataService<Company, MyDataContext>(); otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" }); } }
Perhatikan penggunaan kembali layanan yang sama dengan jenis yang berbeda dalam metode DoSomething di atas. Benar-benar elegan!
Ada banyak alasan bagus lainnya untuk menggunakan obat generik untuk pekerjaan Anda, ini adalah favorit saya.
sumber
Saya suka mereka karena mereka memberi Anda cara cepat untuk menentukan jenis kustom (seperti yang saya gunakan).
Jadi misalnya, alih-alih mendefinisikan struktur yang terdiri dari string dan integer, lalu harus mengimplementasikan seluruh kumpulan objek dan metode tentang cara mengakses array dari struktur tersebut dan seterusnya, Anda cukup membuat Dictionary
Dictionary<int, string> dictionary = new Dictionary<int, string>();
Dan compiler / IDE melakukan tugas berat lainnya. Kamus khususnya memungkinkan Anda menggunakan tipe pertama sebagai kunci (tidak ada nilai yang berulang).
sumber
Koleksi yang diketik - bahkan jika Anda tidak ingin menggunakannya, kemungkinan besar Anda harus menanganinya dari perpustakaan lain, sumber lain.
Pengetikan umum dalam pembuatan kelas:
kelas publik Foo <T> {publik T get () ...
Menghindari casting - Saya selalu tidak menyukai hal-hal seperti
Pembanding baru {public int bandingkanTo (Object o) {if (o instanceof classIcareAbout) ...
Di mana Anda pada dasarnya memeriksa kondisi yang seharusnya hanya ada karena antarmuka diekspresikan dalam bentuk objek.
Reaksi awal saya terhadap obat generik mirip dengan Anda - "terlalu berantakan, terlalu rumit". Pengalaman saya adalah bahwa setelah menggunakannya sebentar Anda akan terbiasa dengan mereka, dan kode tanpa mereka terasa kurang jelas ditentukan, dan hanya kurang nyaman. Selain itu, seluruh dunia java menggunakannya sehingga Anda harus mengikuti program pada akhirnya, bukan?
sumber
Memberi contoh yang baik. Bayangkan Anda memiliki kelas bernama Foo
public class Foo { public string Bar() { return "Bar"; } }
Contoh 1 Sekarang Anda ingin memiliki koleksi objek Foo. Anda memiliki dua opsi, LIst atau ArrayList, keduanya bekerja dengan cara yang sama.
Arraylist al = new ArrayList(); List<Foo> fl = new List<Foo>(); //code to add Foos al.Add(new Foo()); f1.Add(new Foo());
Dalam kode di atas, jika saya mencoba menambahkan kelas FireTruck alih-alih Foo, ArrayList akan menambahkannya, tetapi Daftar Generik Foo akan menyebabkan pengecualian untuk dilempar.
Contoh dua.
Sekarang Anda memiliki dua daftar array dan Anda ingin memanggil fungsi Bar () pada masing-masing. Karena hte ArrayList diisi dengan Objek, Anda harus membuangnya sebelum Anda dapat memanggil bilah. Tetapi karena Daftar Umum Foo hanya dapat berisi Foo, Anda dapat memanggil Bar () langsung pada Foo tersebut.
foreach(object o in al) { Foo f = (Foo)o; f.Bar(); } foreach(Foo f in fl) { f.Bar(); }
sumber
Pernahkah Anda menulis metode (atau kelas) di mana konsep kunci dari metode / kelas tidak terikat erat dengan tipe data tertentu dari parameter / variabel contoh (pikirkan daftar tertaut, fungsi maks / menit, pencarian biner , dll.).
Pernahkah Anda berharap dapat menggunakan kembali algorthm / kode tanpa menggunakan cut-n-paste reuse atau mengorbankan pengetikan yang kuat (misalnya saya ingin salah satu
List
dari Strings, bukan salahList
satu hal yang saya harap adalah string!)?Itulah mengapa Anda harus ingin menggunakan obat generik (atau sesuatu yang lebih baik).
sumber
Jangan lupa bahwa obat generik tidak hanya digunakan oleh kelas, tetapi juga dapat digunakan oleh metode. Misalnya, ambil cuplikan berikut:
private <T extends Throwable> T logAndReturn(T t) { logThrowable(t); // some logging method that takes a Throwable return t; }
Ini sederhana, tetapi bisa digunakan dengan sangat elegan. Hal yang menyenangkan adalah bahwa metode ini mengembalikan apa pun yang diberikan. Ini membantu saat Anda menangani pengecualian yang perlu dikembalikan ke pemanggil:
... } catch (MyException e) { throw logAndReturn(e); }
Intinya adalah Anda tidak kehilangan tipe dengan meneruskannya melalui suatu metode. Anda dapat menampilkan jenis pengecualian yang benar, bukan hanya a
Throwable
, yang bisa Anda lakukan tanpa obat generik.Ini hanyalah contoh sederhana dari satu penggunaan untuk metode umum. Ada beberapa hal rapi lainnya yang dapat Anda lakukan dengan metode umum. Yang paling keren, menurut saya, adalah tipe menyimpulkan dengan obat generik. Ambil contoh berikut (diambil dari Josh Bloch's Effective Java 2nd Edition):
... Map<String, Integer> myMap = createHashMap(); ... public <K, V> Map<K, V> createHashMap() { return new HashMap<K, V>(); }
Ini tidak banyak membantu, tetapi mengurangi beberapa kekacauan ketika tipe generiknya panjang (atau bersarang; yaitu
Map<String, List<String>>
).sumber
Throwable
dari dalam tubuh metode yang memiliki pengecualian khusus yang dinyatakan. Alternatifnya adalah menulis metode terpisah untuk mengembalikan setiap jenis pengecualian, atau melakukan transmisi sendiri dengan metode non-umum yang mengembalikan fileThrowable
. Yang pertama terlalu bertele-tele dan sangat tidak berguna dan yang terakhir tidak akan mendapatkan bantuan dari kompiler. Dengan menggunakan obat generik, kompiler akan secara otomatis memasukkan cast yang benar untuk Anda. Jadi, pertanyaannya adalah: apakah itu sebanding dengan kerumitannya?Keuntungan utama, seperti yang ditunjukkan Mitchel, adalah pengetikan yang kuat tanpa perlu mendefinisikan banyak kelas.
Dengan cara ini Anda dapat melakukan hal-hal seperti:
List<SomeCustomClass> blah = new List<SomeCustomClass>(); blah[0].SomeCustomFunction();
Tanpa obat generik, Anda harus mentransmisikan bla [0] ke jenis yang benar untuk mengakses fungsinya.
sumber
jvm tetap melakukan cast ... ia secara implisit membuat kode yang memperlakukan tipe generik sebagai "Object" dan membuat cast ke instance yang diinginkan. Generik Java hanyalah gula sintaksis.
Saya tahu ini adalah pertanyaan C #, tetapi obat generik juga digunakan dalam bahasa lain, dan kegunaan / tujuannya sangat mirip.
Koleksi Java menggunakan generik sejak Java 1.5. Jadi, tempat yang baik untuk menggunakannya adalah saat Anda membuat objek seperti koleksi Anda sendiri.
Contoh yang saya lihat hampir di mana-mana adalah kelas Pair, yang menampung dua objek, tetapi perlu menangani objek-objek itu dengan cara yang umum.
class Pair<F, S> { public final F first; public final S second; public Pair(F f, S s) { first = f; second = s; } }
Setiap kali Anda menggunakan kelas Pair ini, Anda dapat menentukan jenis objek yang Anda inginkan untuk ditangani dan masalah cast jenis apa pun akan muncul pada waktu kompilasi, bukan waktu proses.
Generik juga dapat memiliki batasan yang ditentukan dengan kata kunci 'super' dan 'extends'. Misalnya, jika Anda ingin berurusan dengan tipe generik tetapi Anda ingin memastikannya memperluas kelas yang disebut Foo (yang memiliki metode setTitle):
public class FooManager <F extends Foo>{ public void setTitle(F foo, String title) { foo.setTitle(title); } }
Meskipun tidak terlalu menarik, penting untuk diketahui bahwa setiap kali Anda berurusan dengan FooManager, Anda tahu bahwa FooManager akan menangani tipe MyClass, dan MyClass memperluas Foo.
sumber
Dari dokumentasi Sun Java, sebagai tanggapan atas "mengapa saya harus menggunakan obat generik?":
"Generik menyediakan cara bagi Anda untuk mengkomunikasikan jenis koleksi ke kompilator, sehingga dapat diperiksa. Setelah kompilator mengetahui jenis elemen dari koleksi, kompilator dapat memeriksa bahwa Anda telah menggunakan koleksi secara konsisten dan dapat menyisipkan cor yang benar pada nilai yang diambil dari koleksi ... Kode yang menggunakan generik lebih jelas dan lebih aman .... kompiler dapat memverifikasi pada waktu kompilasi bahwa batasan jenis tidak dilanggar pada waktu proses [penekanan saya]. Karena kompilasi program tanpa peringatan, kita dapat menyatakan dengan pasti bahwa itu tidak akan memunculkan ClassCastException pada saat dijalankan. Efek bersih dari penggunaan generik, terutama dalam program besar, adalah peningkatan keterbacaan dan ketahanan . [penekanan saya] "
sumber
Generik memungkinkan Anda membuat objek yang sangat diketik, namun Anda tidak perlu menentukan tipe spesifik. Saya pikir contoh terbaik yang berguna adalah List dan kelas serupa.
Menggunakan daftar umum Anda dapat memiliki Daftar Daftar Daftar apa pun yang Anda inginkan dan Anda selalu dapat merujuk pada pengetikan yang kuat, Anda tidak perlu mengonversi atau apa pun seperti yang Anda lakukan dengan Array atau Daftar standar.
sumber
Generik memungkinkan Anda menggunakan pengetikan yang kuat untuk objek dan struktur data yang dapat menampung objek apa pun. Ini juga menghilangkan typecast yang membosankan dan mahal saat mengambil objek dari struktur generik (tinju / unboxing).
Salah satu contoh yang menggunakan keduanya adalah daftar tertaut. Apa gunanya kelas daftar tertaut jika hanya bisa menggunakan objek Foo? Untuk mengimplementasikan daftar tertaut yang dapat menangani segala jenis objek, daftar tertaut dan node dalam kelas dalam node hipotetis harus bersifat umum jika Anda ingin daftar hanya berisi satu jenis objek.
sumber
Jika koleksi Anda berisi tipe nilai, mereka tidak perlu mengemas / melepas kotak ke objek saat dimasukkan ke dalam koleksi sehingga kinerja Anda meningkat secara dramatis. Add-on keren seperti resharper dapat menghasilkan lebih banyak kode untuk Anda, seperti foreach loop.
sumber
Keuntungan lain menggunakan Generik (terutama dengan Koleksi / Daftar) adalah Anda mendapatkan Pemeriksaan Jenis Waktu Kompilasi. Ini sangat berguna saat menggunakan Daftar Generik daripada Daftar Objek.
sumber
Satu-satunya alasan adalah mereka menyediakan keamanan Tipe
List<Customer> custCollection = new List<Customer>;
sebagai lawan,
object[] custCollection = new object[] { cust1, cust2 };
sebagai contoh sederhana.
sumber
Singkatnya, obat generik memungkinkan Anda untuk menentukan lebih tepat apa yang ingin Anda lakukan (pengetikan lebih kuat).
Ini memiliki beberapa manfaat untuk Anda:
Karena kompilator mengetahui lebih banyak tentang apa yang ingin Anda lakukan, ini memungkinkan Anda untuk menghilangkan banyak pengecoran tipe karena kompiler sudah tahu bahwa tipe tersebut akan kompatibel.
Ini juga memberi Anda umpan balik lebih awal tentang kebenaran program Anda. Hal-hal yang sebelumnya akan gagal pada waktu proses (misalnya, karena sebuah objek tidak dapat di-cast dalam jenis yang diinginkan), sekarang gagal pada waktu kompilasi dan Anda dapat memperbaiki kesalahan sebelum departemen pengujian mengajukan laporan bug kriptikal.
Kompiler dapat melakukan lebih banyak pengoptimalan, seperti menghindari tinju, dll.
sumber
Beberapa hal untuk ditambahkan / dikembangkan (berbicara dari sudut pandang .NET):
Tipe generik memungkinkan Anda membuat kelas dan antarmuka berbasis peran. Ini telah dikatakan sudah dalam istilah yang lebih mendasar, tetapi saya menemukan Anda mulai merancang kode Anda dengan kelas yang diimplementasikan dengan cara tipe-agnostik - yang menghasilkan kode yang sangat dapat digunakan kembali.
Argumen umum tentang metode dapat melakukan hal yang sama, tetapi argumen tersebut juga membantu menerapkan prinsip "Katakan Jangan Tanya" untuk casting, yaitu "berikan apa yang saya inginkan, dan jika Anda tidak bisa, beri tahu saya alasannya".
sumber
Saya menggunakannya sebagai contoh di sebuah GenericDao yang diimplementasikan dengan SpringORM dan Hibernate yang terlihat seperti ini
public abstract class GenericDaoHibernateImpl<T> extends HibernateDaoSupport { private Class<T> type; public GenericDaoHibernateImpl(Class<T> clazz) { type = clazz; } public void update(T object) { getHibernateTemplate().update(object); } @SuppressWarnings("unchecked") public Integer count() { return ((Integer) getHibernateTemplate().execute( new HibernateCallback() { public Object doInHibernate(Session session) { // Code in Hibernate for getting the count } })); } . . . }
Dengan menggunakan generik, implementasi saya dari DAO ini memaksa pengembang untuk meneruskan mereka hanya entitas yang dirancang untuk mereka hanya dengan subclass GenericDao
public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> { public UserDaoHibernateImpl() { super(User.class); // This is for giving Hibernate a .class // work with, as generics disappear at runtime } // Entity specific methods here }
Kerangka kecil saya lebih kuat (memiliki hal-hal seperti pemfilteran, pemuatan lambat, pencarian). Saya baru saja menyederhanakan di sini untuk memberi Anda contoh
Saya, seperti Steve dan Anda, mengatakan di awal "Terlalu berantakan dan rumit" tetapi sekarang saya melihat kelebihannya
sumber
Manfaat yang jelas seperti "type safety" dan "no casting" sudah disebutkan, jadi mungkin saya bisa berbicara tentang beberapa "manfaat" lain yang saya harap ini membantu.
Pertama-tama, generik adalah konsep bahasa-independen dan, IMO, mungkin lebih masuk akal jika Anda memikirkan polimorfisme reguler (waktu proses) pada saat yang sama.
Sebagai contoh, polimorfisme seperti yang kita ketahui dari desain berorientasi objek memiliki gagasan runtime di mana objek pemanggil ditemukan pada saat runtime saat eksekusi program berjalan dan metode yang relevan dipanggil sesuai dengan jenis runtime. Dalam obat generik, idenya agak mirip tetapi semuanya terjadi pada waktu kompilasi. Apa artinya itu dan bagaimana Anda memanfaatkannya?
(Mari tetap menggunakan metode umum untuk membuatnya tetap kompak) Ini berarti Anda masih dapat memiliki metode yang sama pada kelas terpisah (seperti yang Anda lakukan sebelumnya di kelas polimorfik) tetapi kali ini mereka dibuat secara otomatis oleh kompilator bergantung pada jenis yang ditetapkan pada waktu kompilasi. Anda mengatur metode Anda pada tipe yang Anda berikan pada waktu kompilasi. Jadi, alih-alih menulis metode dari awal untuk setiap jenis Anda miliki seperti yang Anda lakukan dalam polimorfisme runtime ( metode), Anda membiarkan kompiler melakukan pekerjaan selama kompilasi. Ini memiliki keuntungan yang jelas karena Anda tidak perlu menyimpulkan semua kemungkinan jenis yang mungkin digunakan di sistem Anda yang membuatnya jauh lebih skalabel tanpa perubahan kode.
Kelas bekerja dengan cara yang hampir sama. Anda parametrise tipe dan kode yang dihasilkan oleh kompilator.
Setelah Anda mendapatkan ide tentang "waktu kompilasi", Anda dapat menggunakan jenis "terbatas" dan membatasi apa yang dapat diteruskan sebagai jenis parameter melalui kelas / metode. Jadi, Anda dapat mengontrol apa yang akan dilalui yang merupakan hal yang kuat terutama Anda memiliki kerangka yang dikonsumsi oleh orang lain.
public interface Foo<T extends MyObject> extends Hoo<T>{ ... }
Tidak ada yang bisa menyetel sth selain MyObject sekarang.
Selain itu, Anda bisa "menerapkan" batasan jenis pada argumen metode Anda yang berarti Anda bisa memastikan kedua argumen metode Anda akan bergantung pada tipe yang sama.
public <T extends MyObject> foo(T t1, T t2){ ... }
Semoga semua ini masuk akal.
sumber
Saya pernah memberikan ceramah tentang topik ini. Anda dapat menemukan slide, kode, dan rekaman audio saya di http://www.adventuresinsoftware.com/generics/ .
sumber
Menggunakan obat generik untuk koleksi sangatlah sederhana dan bersih. Bahkan jika Anda menaruhnya di tempat lain, keuntungan dari koleksi adalah kemenangan bagi saya.
List<Stuff> stuffList = getStuff(); for(Stuff stuff : stuffList) { stuff.do(); }
vs.
List stuffList = getStuff(); Iterator i = stuffList.iterator(); while(i.hasNext()) { Stuff stuff = (Stuff)i.next(); stuff.do(); }
atau
List stuffList = getStuff(); for(int i = 0; i < stuffList.size(); i++) { Stuff stuff = (Stuff)stuffList.get(i); stuff.do(); }
Itu saja sepadan dengan "biaya" marjinal obat generik, dan Anda tidak perlu menjadi Guru generik untuk menggunakan ini dan mendapatkan nilai.
sumber
sumber
Generik juga memberi Anda kemampuan untuk membuat lebih banyak objek / metode yang dapat digunakan kembali sambil tetap memberikan dukungan khusus jenis. Anda juga mendapatkan banyak performa dalam beberapa kasus. Saya tidak tahu spesifikasi lengkapnya tentang Java Generics, tetapi di .NET saya dapat menentukan batasan pada parameter Type, seperti Menerapkan Antarmuka, Pembuat, dan Turunan.
sumber
Mengaktifkan pemrogram untuk mengimplementasikan algoritme generik - Dengan menggunakan algoritme generik, pemrogram dapat mengimplementasikan algoritme generik yang bekerja pada kumpulan jenis berbeda, dapat disesuaikan, dan aman untuk tipe serta lebih mudah dibaca.
Pemeriksaan jenis yang lebih kuat pada waktu kompilasi - Pengompilasi Java menerapkan pemeriksaan jenis yang kuat ke kode umum dan mengeluarkan kesalahan jika kode tersebut melanggar keamanan jenis. Memperbaiki kesalahan waktu kompilasi lebih mudah daripada memperbaiki kesalahan waktu proses, yang mungkin sulit ditemukan.
Penghapusan gips.
sumber