Bu beyzbolu parkın dışına atmak isteyenlere teklif edeceğimi düşündüm. Jenerik nedir, jeneriklerin avantajları nelerdir, neden, nerede, nasıl kullanmalıyım? Lütfen oldukça basit tutun. Teşekkürler.
Bu beyzbolu parkın dışına atmak isteyenlere teklif edeceğimi düşündüm. Jenerik nedir, jeneriklerin avantajları nelerdir, neden, nerede, nasıl kullanmalıyım? Lütfen oldukça basit tutun. Teşekkürler.
Yanıtlar:
Kendimi tekrar etmekten gerçekten nefret ediyorum. Aynı şeyi yapmak zorunda olduğumdan daha sık yazmaktan nefret ediyorum. Küçük farklılıkları olan şeyleri defalarca yeniden ifade etmeyi sevmiyorum.
Oluşturmak yerine:
class MyObjectList {
MyObject get(int index) {...}
}
class MyOtherObjectList {
MyOtherObject get(int index) {...}
}
class AnotherObjectList {
AnotherObject get(int index) {...}
}
Yeniden kullanılabilir bir sınıf oluşturabilirim ... (ham koleksiyonu herhangi bir nedenle kullanmak istemediğiniz durumda)
class MyList<T> {
T get(int index) { ... }
}
Şimdi 3 kat daha verimliyim ve sadece bir kopya tutmam gerekiyor. Neden daha az kod kullanmak istemezsiniz?
Bu, diğer sınıflarla etkileşime girmesi gereken a Callable<T>
veya a gibi koleksiyon dışı sınıflar için de geçerlidir Reference<T>
. Yazım açısından güvenli sürümler oluşturmak için gerçekten genişletmeyi Callable<T>
ve Future<T>
diğer tüm ilişkili sınıfları gerçekten istiyor musunuz ?
Yapmıyorum.
Yazmaya gerek kalmaması Java jeneriklerinin en büyük avantajlarından biridir , çünkü derleme sırasında tür denetimi gerçekleştirir. Bu, olasılığını azaltacaktırClassCastException
, çalışma zamanında atılabilecek daha sağlam bir koda yol açabilir.
Ama bunun tamamen farkında olduğundan şüpheleniyorum.
Generics'e her baktığımda başımı ağrıtıyor. Java'nın en iyi kısmının basit olması ve minimum sözdizimi ve jeneriklerin basit olmaması ve önemli miktarda yeni sözdizimi eklemesi olduğunu düşünüyorum.
İlk başta jenerik ilaçların faydasını da görmedim. Java'yı 1.4 sözdiziminden öğrenmeye başladım (o sırada Java 5 çıkmış olsa da) ve jeneriklerle karşılaştığımda, yazmanın daha fazla kod olduğunu hissettim ve faydalarını gerçekten anlamadım.
Modern IDE'ler, jeneriklerle kod yazmayı kolaylaştırır.
Modern, düzgün IDE'lerin çoğu, özellikle kod tamamlama konusunda jeneriklerle kod yazmaya yardımcı olacak kadar akıllıdır.
İşte bir Map<String, Integer>
ile bir HashMap
. Yazmam gereken kod:
Map<String, Integer> m = new HashMap<String, Integer>();
Ve gerçekten, yeni bir şey yapmak için yazmak çok fazla HashMap
. Ancak gerçekte Eclipse neye ihtiyacım olduğunu bilmeden önce bu kadarını yazmam gerekiyordu:
Map<String, Integer> m = new Ha
Ctrl+Space
Doğru, HashMap
bir aday listesinden seçim yapmam gerekiyordu , ancak temelde IDE genel tipler de dahil olmak üzere ne ekleyeceğini biliyordu. Doğru araçlarla jenerik kullanmak o kadar da kötü değil.
Buna ek olarak, türler bilindiğinden, genel koleksiyondan öğeleri alırken, IDE, bu nesne zaten bildirilen türünün bir nesnesiymiş gibi davranacaktır - nesnenin türünü bilmek için IDE'nin dönüştürülmesine gerek yoktur. dır-dir.
Jeneriklerin önemli bir avantajı, yeni Java 5 özellikleriyle iyi bir şekilde oynama biçiminden gelir. İşte tamsayıları a'ya atmaya Set
ve toplamını hesaplamaya bir örnek :
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);
int total = 0;
for (int i : set) {
total += i;
}
Bu kod parçasında, üç yeni Java 5 özelliği bulunmaktadır:
İlk olarak, ilkellerin jenerikleri ve otomatik kutulaması aşağıdaki satırlara izin verir:
set.add(10);
set.add(42);
Tam sayı 10
, bir içine autoboxed edilir Integer
değeri ile 10
. (Ve aynı 42
). Sonra bu Integer
, s Set
tuttuğu bilinen şeyin içine atılır Integer
. Bir atmaya çalışmak String
derleme hatasına neden olur.
Daha sonra, her döngü için bunların üçünü de alır:
for (int i : set) {
total += i;
}
İlk olarak, her bir döngü için Set
içeren Integer
s kullanılır. Her elemanın bir olduğu bildirilir int
ve bu Integer
, ilkele döndürüldüğü için buna izin verilir int
. Ve bu kutudan çıkarma jenerik olduğunu belirtmek için kullanılır, çünkü bilindiği oluşur gerçeği Integer
düzenlenen s Set
.
Jenerikler, Java 5'te sunulan yeni özellikleri bir araya getiren yapıştırıcı olabilir ve kodlamayı daha basit ve daha güvenli hale getirir. Ve çoğu zaman IDE'ler size iyi önerilerde yardımcı olacak kadar akıllıdır, bu nedenle genellikle daha fazla yazı yazmaz.
Ve açıkçası, Set
örnekten de görülebileceği gibi, Java 5 özelliklerini kullanmanın kodu daha kısa ve güçlü hale getirebileceğini düşünüyorum.
Düzenleme - Jenerik içermeyen bir örnek
Aşağıda, Set
jenerik kullanılmadan yukarıdaki örneğin bir açıklaması bulunmaktadır . Mümkün, ancak tam olarak hoş değil:
Set set = new HashSet();
set.add(10);
set.add(42);
int total = 0;
for (Object o : set) {
total += (Integer)o;
}
(Not: Yukarıdaki kod, derleme zamanında denetlenmemiş dönüşüm uyarısı oluşturacaktır.)
Genel olmayan koleksiyonları kullanırken, koleksiyona girilen türler, tür nesneleridir Object
. Bu nedenle, bu örnekte, sete eklenen Object
şey add
a'dır.
set.add(10);
set.add(42);
Yukarıdaki satırlarda, otomatik kutulama oyunda - ilkel int
değer 10
ve nesnelere 42
otomatik olarak kutuya alınıyor Integer
, bunlar Set
. Ancak, derleyicinin ne tür beklemesi gerektiğini bilmesine yardımcı olacak hiçbir tür bilgisi olmadığından , Integer
nesnelerin Object
s olarak işlendiğini unutmayın Set
.
for (Object o : set) {
Önemli olan kısım budur. Her bir için döngünün çalışmasının nedeni Set
, Iterable
arabirimin Iterator
, varsa, bir ile tür bilgisi döndüren arabirimi gerçekleştirmesidir . ( Iterator<T>
yani.)
Tür bilgi yok olduğundan, ancak, Set
bir döner Iterator
değerleri dönecektir hangi Set
olarak Object
s ve bu eleman için-her döngüde alındığını nedenle gereken tipte Object
.
Şimdi Object
öğesinden alındığına göre Set
, Integer
eklemeyi gerçekleştirmek için manuel olarak dönüştürülmesi gerekir:
total += (Integer)o;
Burada, bir'den Object
diğerine bir typecast gerçekleştirilir Integer
. Bu durumda, bunun her zaman işe yarayacağını biliyoruz, ancak manuel tipleme bana her zaman, başka bir yerde küçük bir değişiklik yapılırsa zarar görebilecek kırılgan kod olduğunu hissettiriyor. (Her typecast'in ClassCastException
gerçekleşmeyi beklediğini hissediyorum , ama konu dışına çıkıyorum ...)
Integer
Şimdi içine Kutusuz edilir int
ve içine eklenmesini gerçekleştirmek için izin int
değişken total
.
Umarım Java 5'in yeni özelliklerinin jenerik olmayan kodlarla kullanılabileceğini gösterebilirim, ancak jeneriklerle kod yazmak kadar temiz ve basit değildir. Ve benim düşünceme göre, Java 5'teki yeni özelliklerden tam olarak yararlanmak için, jeneriklere bakmak gerekir, en azından, çalışma zamanında istisnalar atmak için geçersiz tip yayınlarını önlemek için derleme zamanı kontrollerine izin verir.
Eğer 1.5 yayınlandı hemen önce Java hata veritabanında arama olsaydı, sen yedi kat daha fazla hata bulmak istiyorum NullPointerException
daha ClassCastException
. Bu nedenle, böcekleri veya en azından küçük bir duman testinden sonra devam eden böcekleri bulmak harika bir özellik gibi görünmüyor.
Benim için jeneriklerin büyük avantajı, önemli tür bilgilerini kodda belgelemeleri . Bu tür bilginin kodda belgelenmesini istemeseydim, dinamik olarak yazılmış bir dil veya en azından daha örtük tür çıkarımına sahip bir dil kullanırdım.
Bir nesnenin koleksiyonlarını kendine saklamak kötü bir stil değildir (ancak o zaman yaygın olan stil, kapsüllemeyi etkili bir şekilde görmezden gelmektir). Daha çok ne yaptığınıza bağlıdır. Koleksiyonları "algoritmalara" aktarmak, jeneriklerle kontrol etmek (derleme zamanında veya öncesinde) biraz daha kolaydır.
Java'daki jenerikler parametrik polimorfizmi kolaylaştırır . Tür parametreleri aracılığıyla türlere argümanlar iletebilirsiniz. Tıpkı bir yöntemin String foo(String s)
bazı davranışları modellemesi gibi , sadece belirli bir dizge için değil, herhangi bir dizge için s
, yani bir tür List<T>
, yalnızca belirli bir tür için değil , herhangi bir tür için de bazı davranışları modeller . List<T>
diyor herhangi bir tür için T
, bir tür var List
olan unsurlardır T
s . Yani List
aslında bir tür kurucusu . Bir türü argüman olarak alır ve sonuç olarak başka bir türü oluşturur.
İşte her gün kullandığım genel türlere birkaç örnek. İlk olarak, çok kullanışlı bir genel arayüz:
public interface F<A, B> {
public B f(A a);
}
Bu arayüz söylüyor yaklaşık iki türleri için, A
ve B
, (adında bir fonksiyon var f
bir sürer) A
ve döndürür B
. Birincisini alıp ikincisini döndüren bir işlev sağladığınız sürece , bu arabirimi uyguladığınızda A
ve B
istediğiniz herhangi bir tür olabilir f
. Arayüzün örnek bir uygulaması:
F<Integer, String> intToString = new F<Integer, String>() {
public String f(int i) {
return String.valueOf(i);
}
}
Jeneriklerden önce, polimorfizm anahtar kelime kullanılarak alt sınıflandırma ile elde ediliyordu extends
. Jeneriklerle, aslında alt sınıflamayı ortadan kaldırabilir ve bunun yerine parametrik polimorfizm kullanabiliriz. Örneğin, herhangi bir tür için karma kodların hesaplanmasında kullanılan parametreli (genel) bir sınıfı düşünün. Object.hashCode () 'u geçersiz kılmak yerine, bunun gibi genel bir sınıf kullanırdık:
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);
}
}
Bu, kalıtımı kullanmaktan çok daha esnektir, çünkü kırılgan hiyerarşileri kilitlemeden kompozisyon ve parametrik polimorfizm kullanma temasında kalabiliriz.
Java'nın jenerikleri mükemmel değil. Türleri soyutlayabilirsiniz, ancak örneğin tür oluşturucular üzerinde soyutlama yapamazsınız. Yani, "herhangi bir T türü için" diyebilirsiniz, ancak "A tipi parametresi alan herhangi bir T türü için" diyemezsiniz.
Java jeneriklerinin bu sınırları hakkında burada bir makale yazdım.
Jenerik ilaçların büyük bir kazanımı, alt sınıflardan kaçınmanıza izin vermeleridir. Alt sınıflandırma, genişletilmesi zor olan kırılgan sınıf hiyerarşileriyle ve tüm hiyerarşiye bakmadan tek tek anlaşılması zor sınıflarla sonuçlanma eğilimindedir.
Jenerik önce wereas sizin gibi sınıflar olabilir Widget
uzatıldı FooWidget
, BarWidget
ve BazWidget
, jenerik ile tek bir jenerik sınıfı olabilir Widget<A>
bir sürer Foo
, Bar
ya Baz
da yapıcı vermek için de Widget<Foo>
, Widget<Bar>
ve Widget<Baz>
.
Jenerikler, boks ve kutudan çıkarmanın performans vuruşundan kaçınır. Temel olarak, ArrayList ile List <T> karşılaştırmasına bakın. Her ikisi de aynı temel şeyleri yapar, ancak List <T> çok daha hızlı olacaktır çünkü nesneye / nesneden kutuya kutuya gitmeniz gerekmez.
Generics'in en iyi yararı, kodun yeniden kullanılmasıdır. Diyelim ki çok sayıda iş nesneniz var ve aynı eylemleri gerçekleştirmek için her varlık için ÇOK benzer kod yazacaksınız. (IE Linq to SQL işlemleri).
Jeneriklerle, belirli bir temel sınıftan miras alan türlerden herhangi biri verildiğinde çalışabilecek bir sınıf oluşturabilir veya aşağıdaki gibi belirli bir arabirimi uygulayabilirsiniz:
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" });
}
}
Yukarıdaki DoSomething yönteminde farklı Türler verildiğinde aynı hizmetin yeniden kullanıldığına dikkat edin. Gerçekten zarif!
Çalışmanız için jenerik kullanmanın başka birçok harika nedeni var, bu benim favorim.
Onlardan hoşlanıyorum çünkü size özel bir tür tanımlamak için hızlı bir yol sunuyorlar (zaten kullandığım için).
Örneğin, bir dizi ve bir tamsayıdan oluşan bir yapı tanımlamak ve daha sonra bu yapıların bir dizisine nasıl erişileceğine ilişkin bir dizi nesne ve yöntem uygulamak zorunda kalmak yerine, bir Sözlük yapabilirsiniz.
Dictionary<int, string> dictionary = new Dictionary<int, string>();
Ve derleyici / IDE, işin geri kalanını halleder. Özellikle bir Sözlük, ilk türü anahtar olarak kullanmanıza izin verir (tekrarlanan değerler yoktur).
Yazılı koleksiyonlar - onları kullanmak istemeseniz bile, diğer kütüphanelerden, diğer kaynaklardan onlarla ilgilenmeniz gerekebilir.
Sınıf oluşturmada genel yazım:
public class Foo <T> {public T get () ...
Dökümden kaçınma - her zaman böyle şeylerden hoşlanmadım
yeni Karşılaştırıcı {public int CompareTo (Object o) {if (o instanceof classIcareAbout) ...
Esasen, sadece arayüz nesneler cinsinden ifade edildiği için var olması gereken bir koşulu kontrol ettiğiniz yer.
Jenerik ilaçlara ilk tepkim sizinkine benzerdi - "çok dağınık, çok karmaşık". Tecrübelerime göre, onları bir süre kullandıktan sonra onlara alışırsınız ve onlarsız kodlama daha az açıkça belirtilmiş ve daha az rahat hissettirir. Bunun dışında, java dünyasının geri kalanı onları kullanıyor, bu yüzden sonunda programa girmeniz gerekecek, değil mi?
İyi bir örnek vermek gerekirse. Foo adında bir sınıfınız olduğunu hayal edin
public class Foo
{
public string Bar() { return "Bar"; }
}
Örnek 1 Şimdi Foo nesnelerinin bir koleksiyonuna sahip olmak istiyorsunuz. Her ikisi de benzer şekilde çalışan iki seçeneğiniz var, LIst veya ArrayList.
Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();
//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());
Yukarıdaki kodda, Foo yerine bir FireTruck sınıfı eklemeye çalışırsam, ArrayList onu ekleyecektir, ancak Generic List of Foo bir istisna atılmasına neden olacaktır.
Örnek iki.
Artık iki dizi listeniz var ve her birinde Bar () işlevini çağırmak istiyorsunuz. Hte ArrayList, Objects ile dolu olduğundan, bar'ı çağırmadan önce onları çevirmeniz gerekir. Ancak, Genel Foo Listesi yalnızca Foos içerebileceğinden, Bar () 'ı doğrudan bunlardan çağırabilirsiniz.
foreach(object o in al)
{
Foo f = (Foo)o;
f.Bar();
}
foreach(Foo f in fl)
{
f.Bar();
}
Daha önce yöntemin / sınıfın temel kavramının parametrelerin / örnek değişkenlerin belirli bir veri türüne sıkı sıkıya bağlı olmadığı bir yöntem (veya bir sınıf) yazmadınız mı (bağlantılı liste, maks / min işlevleri, ikili arama düşünün) , vb.).
Cut-n-paste yeniden kullanımına başvurmadan veya güçlü-yazmadan ödün vermeden algoritmayı / kodu yeniden kullanabilmeyi hiç istemediniz mi (örn. Dizelerden bir tane istiyorum , dizge olduğunu umduğum şeylerden List
değil !)?List
Eğer gereken yüzden en istemek Generics'i (ya da bir şey daha iyi) kullanmak için.
Jeneriklerin sadece sınıflar tarafından kullanılmadığını, aynı zamanda yöntemler tarafından da kullanılabileceğini unutmayın. Örneğin, aşağıdaki pasajı alın:
private <T extends Throwable> T logAndReturn(T t) {
logThrowable(t); // some logging method that takes a Throwable
return t;
}
Basittir, ancak çok şık bir şekilde kullanılabilir. Güzel olan şey, yöntemin verildiği her şeyi geri vermesidir. Bu, arayan kişiye yeniden atılması gereken istisnaları ele aldığınızda yardımcı olur:
...
} catch (MyException e) {
throw logAndReturn(e);
}
Önemli olan, türü bir yöntemden geçirerek kaybetmemenizdir. Sadece a yerine doğru istisna türünü atabilirsiniz Throwable
, bu, jenerikler olmadan yapabileceğiniz tek şey olabilir.
Bu, genel yöntemler için bir kullanımın basit bir örneğidir. Genel yöntemlerle yapabileceğiniz pek çok başka güzel şey var. Bence en havalı, jeneriklerle çıkarım yapan tiptir. Aşağıdaki örneği alın (Josh Bloch'un Etkili Java 2. Sürümünden alınmıştır):
...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
return new HashMap<K, V>();
}
Bu pek bir şey yapmaz, ancak genel türler uzun olduğunda (veya iç içe geçmiş; yani Map<String, List<String>>
) bazı dağınıklığı azaltır .
Throwable
ki, belirli istisnalar bildirilmiş bir yöntem gövdesi içinden düz bir eski atamazsınız. Bunun alternatifi, her bir istisna türünü döndürmek için ayrı yöntemler yazmak veya atamayı kendi başınıza bir Throwable
. İlki çok ayrıntılı ve oldukça kullanışsız ve ikincisi derleyiciden herhangi bir yardım almayacak. Jenerikleri kullanarak, derleyici sizin için otomatik olarak doğru dökümleri ekleyecektir. Öyleyse soru şu: bu karmaşıklığa değer mi?
Mitchel'in belirttiği gibi birincil avantaj, birden çok sınıf tanımlamaya gerek kalmadan güçlü yazımdır.
Bu şekilde aşağıdaki gibi şeyler yapabilirsiniz:
List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();
Jenerikler olmadan, işlevlerine erişmek için blah [0] 'ı doğru türe çevirmeniz gerekir.
jvm yine de yayınlar ... dolaylı olarak genel türü "Nesne" olarak değerlendiren ve istenen somutlaştırmaya yönelik yayınlar oluşturan kod oluşturur. Java jenerikleri sadece sözdizimsel şekerdir.
Bunun bir C # sorusu olduğunu biliyorum, ancak jenerikler diğer dillerde de kullanılıyor ve kullanımları / hedefleri oldukça benzer.
Java koleksiyonları Java 1.5'ten beri jenerik kullanır . Dolayısıyla, kendi koleksiyon benzeri nesnenizi oluşturduğunuz zamandır bunları kullanmak için iyi bir yer.
Hemen hemen her yerde gördüğüm bir örnek, iki nesneyi barındıran, ancak bu nesnelerle genel bir şekilde ilgilenmesi gereken bir Pair sınıfıdır.
class Pair<F, S> {
public final F first;
public final S second;
public Pair(F f, S s)
{
first = f;
second = s;
}
}
Bu Pair sınıfını her kullandığınızda, hangi tür nesnelerle uğraşmasını istediğinizi belirtebilirsiniz ve herhangi bir tür döküm problemi, çalışma zamanı yerine derleme zamanında görünecektir.
Jenerikler ayrıca sınırlarını 'süper' ve 'genişletilmiş' anahtar kelimeleriyle tanımlayabilir. Örneğin, genel bir türle uğraşmak istiyorsanız, ancak Foo adında bir sınıfı genişlettiğinden emin olmak istiyorsanız (setTitle yöntemine sahip):
public class FooManager <F extends Foo>{
public void setTitle(F foo, String title) {
foo.setTitle(title);
}
}
Kendi başına çok ilginç olmasa da, bir FooManager ile her uğraştığınızda, MyClass türlerini işleyeceğini ve MyClass'ın Foo'yu genişleteceğini bilmenizde fayda var.
Sun Java belgelerinden, "neden jenerik kullanmalıyım?" Sorusuna yanıt olarak:
"Generics, bir koleksiyonun türünü derleyiciye iletmeniz için bir yol sağlar, böylece kontrol edilebilir. Derleyici, koleksiyonun öğe türünü bildiğinde, derleyici, koleksiyonu tutarlı bir şekilde kullandığınızı kontrol edebilir ve ekleyebilir Koleksiyondan çıkarılan değerlere doğru atamalar ... Jenerikleri kullanan kod daha açık ve daha güvenlidir .... derleyici derleme zamanında tür kısıtlamalarının çalışma zamanında ihlal edilmediğini doğrulayabilir [vurgu benim]. Çünkü program uyarılar olmadan derler, çalışma zamanında bir ClassCastException oluşturmayacağından emin olabiliriz. Jenerik kullanmanın net etkisi, özellikle büyük programlarda, geliştirilmiş okunabilirlik ve sağlamlıktır . [vurgu benim] "
Jenerikler, güçlü bir şekilde yazılmış nesneler oluşturmanıza izin verir, ancak belirli bir türü tanımlamanız gerekmez. Bence en kullanışlı örnek List ve benzeri sınıflar.
Genel listeyi kullanarak, istediğiniz her şeyi bir Liste Listesi Listesi alabilir ve her zaman güçlü yazıma başvurabilirsiniz, dönüştürmek veya bir Dizi veya standart Liste ile yapacağınız gibi bir şey yapmak zorunda değilsiniz.
Jenerikler, herhangi bir nesneyi tutması gereken nesneler ve veri yapıları için güçlü yazım kullanmanıza izin verir. Ayrıca, genel yapılardan (kutulama / kutudan çıkarma) nesneleri alırken sıkıcı ve pahalı tip yayınlarını ortadan kaldırır.
Her ikisini de kullanan bir örnek, bağlantılı bir listedir. Bağlantılı bir liste sınıfının yalnızca Foo nesnesini kullanması ne işe yarar? Herhangi bir tür nesneyi işleyebilen bağlantılı bir liste uygulamak için, listenin yalnızca bir tür nesne içermesini istiyorsanız, bağlantılı liste ve varsayımsal düğüm iç sınıfındaki düğümler genel olmalıdır.
Generics kullanmanın bir başka avantajı (özellikle Koleksiyonlar / Listelerle) Derleme Süresi Tip Kontrolü'nü elde etmenizdir. Bu, Nesnelerin Listesi yerine Genel Liste kullanırken gerçekten kullanışlıdır.
Çoğu neden, Tip güvenliği sağlamalarıdır
List<Customer> custCollection = new List<Customer>;
aksine,
object[] custCollection = new object[] { cust1, cust2 };
basit bir örnek olarak.
Özetle, jenerikler ne yapmak istediğinizi daha kesin bir şekilde belirlemenize izin verir (daha güçlü yazım).
Bunun sizin için birçok faydası vardır:
Derleyici, ne yapmak istediğiniz hakkında daha fazla şey bildiğinden, çok sayıda tür çevirmeyi atlamanıza izin verir çünkü zaten türün uyumlu olacağını bilir.
Bu aynı zamanda programınızın doğruluğu hakkında size daha erken geri bildirim sağlar. Daha önce çalışma zamanında başarısız olan şeyler (örneğin, bir nesne istenen türde dönüştürülemediği için), şimdi derleme zamanında başarısız olur ve test departmanınız şifreli bir hata raporu vermeden önce hatayı düzeltebilirsiniz.
Derleyici, kutudan kaçınmak gibi daha fazla optimizasyon yapabilir.
Eklenecek / genişletilecek birkaç şey (.NET açısından bakıldığında):
Genel türler, rol tabanlı sınıflar ve arabirimler oluşturmanıza olanak tanır. Bu zaten daha temel terimlerle söylendi, ancak kodunuzu türden bağımsız bir şekilde uygulanan sınıflarla tasarlamaya başladığınızı görüyorum - bu da oldukça yeniden kullanılabilir kodla sonuçlanır.
Yöntemlerle ilgili genel argümanlar da aynı şeyi yapabilir, ancak aynı zamanda "Sorma Söyle" prensibinin döküm işlemine uygulanmasına da yardımcı olur, yani "bana istediğimi ver, yapamazsan bana nedenini söyle".
Bunları, örneğin, SpringORM ve Hibernate ile uygulanan, buna benzeyen bir Generic Dao'da kullanıyorum
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
}
}));
}
.
.
.
}
Jenerikleri kullanarak, bu DAO'ların uygulamalarım, geliştiriciyi, sadece Jenerikdao'nun alt sınıflarını oluşturarak, yalnızca tasarlandıkları varlıkları geçirmeye zorluyor.
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
}
Benim küçük çerçevem daha sağlam (filtreleme, tembel yükleme, arama gibi şeyler var). Size bir örnek vermek için burayı sadeleştirdim
Steve ve senin gibi ben de başta "Çok dağınık ve karmaşık" dedim ama şimdi avantajlarını görüyorum
"Tip güvenliği" ve "döküm yok" gibi bariz avantajlardan daha önce bahsedilmiştir, bu yüzden belki yardımcı olacağını umduğum bazı diğer "faydalardan" bahsedebilirim.
Her şeyden önce, jenerik dilden bağımsız bir kavramdır ve IMO, aynı zamanda normal (çalışma zamanı) polimorfizmi düşünürseniz daha mantıklı olabilir.
Örneğin, nesne yönelimli tasarımdan bildiğimiz gibi polimorfizm, program yürütme devam ederken çağıran nesnenin çalışma zamanında çözüldüğü ve çalışma zamanı türüne bağlı olarak ilgili yöntemin uygun şekilde çağrıldığı bir çalışma zamanı kavramına sahiptir. Jenerikte fikir biraz benzerdir ancak her şey derleme zamanında gerçekleşir. Bu ne anlama geliyor ve bundan nasıl yararlanıyorsunuz?
(Kompakt tutmak için genel yöntemlere bağlı kalalım) Bu, aynı yöntemi ayrı sınıflarda hala kullanabileceğiniz anlamına gelir (daha önce polimorfik sınıflarda yaptığınız gibi), ancak bu sefer derleyici tarafından otomatik olarak üretilirler, ayarlanan türlere bağlıdır. derleme zamanında. Yöntemlerinizi derleme sırasında verdiğiniz türe göre ayarlarsınız. Bu nedenle, her tür için yöntemleri sıfırdan yazmak yerine çalışma zamanı polimorfizminde (yöntemi geçersiz kılma) yaptığınız gibi sahip olduğunuz derleme sırasında derleyicilerin işi yapmasına izin verirsiniz. Sisteminizde kullanılabilecek olası tüm türleri çıkarmanıza gerek olmadığından, bunun açık bir avantajı vardır, bu da onu bir kod değişikliği olmadan çok daha ölçeklenebilir hale getirir.
Sınıflar hemen hemen aynı şekilde çalışır. Türü parametrelersiniz ve kod derleyici tarafından oluşturulur.
"Derleme zamanı" fikrini edindikten sonra, "sınırlı" türleri kullanabilir ve sınıflar / yöntemler aracılığıyla parametreleştirilmiş bir tür olarak iletilebilecekleri kısıtlayabilirsiniz. Böylece, neyin içinden geçirileceğini kontrol edebilirsiniz, bu güçlü bir şey, özellikle de başkaları tarafından tüketilen bir çerçeveniz var.
public interface Foo<T extends MyObject> extends Hoo<T>{
...
}
Artık MyObject dışında kimse bir şey ayarlayamaz.
Ayrıca, yöntem bağımsız değişkenleriniz üzerinde tür kısıtlamalarını "zorlayabilirsiniz"; bu, her iki yöntem bağımsız değişkeninizin de aynı türe bağlı olduğundan emin olabileceğiniz anlamına gelir.
public <T extends MyObject> foo(T t1, T t2){
...
}
Umarım tüm bunlar mantıklıdır.
Bir keresinde bu konu hakkında bir konuşma yapmıştım. Slaytlarımı, kodumu ve ses kaydımı http://www.adventuresinsoftware.com/generics/ adresinde bulabilirsiniz .
Koleksiyonlar için jenerik kullanmak sadece basit ve temizdir. Başka bir yerde üzerinde oynasanız bile, koleksiyonlardan elde edilen kazanç benim için bir kazançtır.
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();
}
veya
List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
Stuff stuff = (Stuff)stuffList.get(i);
stuff.do();
}
Bu tek başına jenerik ilaçların marjinal "maliyetine" değer ve bunu kullanmak ve değer elde etmek için jenerik bir Guru olmanıza gerek yok.
Jenerikler, türe özgü destek sağlamaya devam ederken, size daha fazla yeniden kullanılabilir nesneler / yöntemler oluşturma yeteneği de sağlar. Ayrıca bazı durumlarda çok fazla performans elde edersiniz. Java Generics ile ilgili tüm özellikleri bilmiyorum, ancak .NET'te bir Arayüz, Oluşturucu ve Türev Uygulama gibi Tür parametresinde kısıtlamalar belirleyebilirim.
Programcıların genel algoritmaları uygulamasına olanak sağlama - Programcılar, jenerikler kullanarak farklı türdeki koleksiyonlar üzerinde çalışan, özelleştirilebilen ve tür açısından güvenli ve okunması daha kolay olan genel algoritmalar uygulayabilir.
Derleme zamanında daha güçlü tür denetimleri - Bir Java derleyicisi, genel koda güçlü tür denetimi uygular ve kod tür güvenliğini ihlal ederse hata verir. Derleme zamanı hatalarını düzeltmek, bulunması zor olabilen çalışma zamanı hatalarını düzeltmekten daha kolaydır.
Dökümlerin ortadan kaldırılması.