Java'nın jeneriklerinde yanlış olan nedir? [kapalı]


49

Java’nın jenerik ürününü uygulamasına son veren bu site yayınlarında birkaç kez gördüm. Şimdi, dürüstçe söyleyebilirim ki bunları kullanma konusunda herhangi bir sorun yaşamadım. Ancak, kendimi genel bir sınıf haline getirmedim. Peki, Java'nın genel desteği ile ilgili sorunlarınız nelerdir?




Clinton Begin ("iBatis Guy") dediği gibi, "işe
yaramazlar

Yanıtlar:


54

Java'nın genel uygulaması tip silme kullanır . Bu, güçlü bir şekilde yazılmış genel koleksiyonlarınızın aslında Objectçalışma zamanında türden olduğu anlamına gelir . İlkel türlerin genel bir koleksiyona eklendiklerinde kutulanması gerektiği anlamına geldiğinden, bunun bazı performans değerlendirmeleri vardır. Tabii ki, derleme zamanı tür doğruluğunun yararları, tip silme ve geriye dönük uyumluluk üzerine obsesif odaklanma genel bereketinden daha ağır basar.


4
Yalnızca silme türü nedeniyle, çalışma zamanında genel bilgileri almak TypeLiteral google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…
Jeremy Heiler

43
Anlamınew ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
Kendine not -

9
@Job hayır değil. Ve C ++, şablon denilen metaprogramlamaya tamamen farklı bir yaklaşım kullanır. Ördek yazmayı kullanır template<T> T add(T v1, T v2) { return v1->add(v2); }ve bunun gibi faydalı şeyler yapabilirsiniz ve orada add, bunlar ne olursa olsun, iki şeyden oluşan bir işlev yaratmanın gerçekten genel bir yolunu bulursunuz , sadece add()bir parametreye sahip bir metoda sahip olmaları gerekir .
Trinidad

7
@Job gerçekten kod üretildi. Şablonlar C önişlemcisinin yerini almak içindir ve bunu yapmak için çok akıllıca bazı püf noktaları yapabilmeli ve kendi başlarına Turing-eksiksiz bir dil olmalıdır. Java jenerikleri yalnızca güvenli tip konteynerler için şekerdir ve C # generikleri daha iyidir, fakat hala fakir bir adamın C ++ şablonu.
Trinidad,

3
@Job AFAIK, Java jenerikler, her yeni jenerik tip için bir sınıf oluşturmaz, sadece jenerik metotlar kullanan koda tiplendirmeler ekler, bu yüzden aslında meta-programlama IMO'su değildir. C # şablonları, her genel tür için yeni kod oluşturur, yani, Cint dünyasında <int> Listesi'ni ve <double> Listesini kullanırsanız, her biri için kod oluşturur. Şablonlara kıyasla, C # jeneriği hala hangi türü besleyebileceğinizi bilmeyi gerektirir. Verdiğim basit Addörneği uygulayamazsınız, önceden hangi derslere uygulanabileceğini, bir dezavantajı olduğunu bilmeden mümkün değildir.
Trinidad,

26

Daha önce verilen cevapların Java dili, JVM ve Java Sınıf Kütüphanesi kombinasyonuna yoğunlaştığını gözlemleyin.

Java'nın jeneriklerinde, Java dili söz konusu olduğunda yanlış bir şey yoktur. C # ve Java jeneriklerinde tanımlandığı gibi, Java'nın jenerikliği , 1. dilde oldukça iyidir .

Süboptimal, JVM'nin doğrudan jenerik ilaçları desteklemediği ve bunun birkaç önemli sonucu olduğu:

  • Sınıf Kitaplığının yansıma ile ilgili bölümleri, Java dilinde mevcut olan tüm tür bilgileri gösteremez.
  • Bazı performans cezaları söz konusudur.

Yaptığım ayrımın dikenli bir dil olduğunu düşünüyorum; Java dili her yerde hedef olarak JVM ile ve çekirdek sınıf kütüphanesi olarak Java Sınıf Kütüphanesi ile derlendi.

1 Genel çıkarmada tip çıkarımı tespit edilemez kıldığına inanılan joker karakterler hariç. Bu, C # ile Java jenerikler arasında çok sık belirtilmeyen önemli bir farktır. Sağol Antimon.


14
JVM'nin jenerikliği desteklemesi gerekmez. Bu, i ++ 86 gerçek makinenin sahte olan şablonları desteklemediği için C ++ 'ın hiç şablona sahip olmadığını söylemekle aynı olacaktır. Sorun, derleyicinin genel türleri uygulamak için uyguladığı yaklaşımdır. Ve yine, C ++ şablonları çalışma zamanında herhangi bir ceza içermiyor, hiç bir yansıma yapılamıyor, sanırım Java'da yapılması gereken tek neden sadece zayıf dil tasarımı.
Trinidad

2
@Trinidad ama Java'nın jenerik olmadığını söylemedi, bu yüzden nasıl aynı olduğunu göremiyorum. Ve evet, JVM vermez gerek C ++ yoktur, tıpkı onları desteklemek için ihtiyaç kodunu optimize etmek. Ancak optimizasyon yapmamak kesinlikle "yanlış bir şey" olarak sayılır.
Roman Starkov

3
Benim iddia ettiğim, JVM'nin jeneriklere doğrudan yansımasını kullanabilmeleri için doğrudan desteğe ihtiyaç duymaması gerektiğidir. Diğerleri bunu yaptı, böylece yapılabilir, sorun Java'nın jeneriklerden yeni sınıflar üretmemesidir, yeniden birleşme söz konusu değildir.
Trinidad

4
@Trinidad: C ++ 'da, derleyiciye yeterli zaman ve bellek verildiği varsayılarak, bir şey yapmak için şablonlar kullanılabilir ve derleme zamanında mevcut olan bilgilerle yapılabilecek her şey (şablonların derlenmesi Turing-tamamlandı olduğundan), ancak program çalıştırılmadan önce mevcut olmayan bilgileri kullanarak şablon oluşturmanın bir yolu yoktur. Net, aksine, bir programın girişine dayalı türler oluşturmak mümkündür; Oluşturulabilecek farklı tür sayısının evrendeki elektron sayısını aştığı bir program yazmak oldukça kolay olurdu.
supercat

2
@Trinidad: Açıkçası, hiçbir programın yürütülmesi, bu türlerin hepsini (veya aslında, sınırsız bir kesiminin ötesindeki herhangi bir şeyi) yaratamaz, ama mesele bunun zorunlu değil. Sadece gerçekten kullanılan türleri yaratması gerekiyor. Birinde herhangi bir rasgele sayıyı (örn. 8675309) Kabul edebilecek bir program olabilir ve ondan Z8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>başka bir türden farklı üyelere sahip olan bu numaraya (örneğin ) özgü bir tür yaratabilir . C ++ 'da, herhangi bir girdiden üretilebilecek tüm tiplerin derleme zamanında üretilmesi gerekir.
supercat

13

Genel eleştiri, yeniden birleşmenin eksikliğidir. Bu, çalışma zamanında nesnelerin genel argümanları hakkında bilgi içermediği anlamına gelir (her ne kadar bilgiler hala alanlar, yöntemler, yapıcılar ve genişletilmiş sınıf ve arayüzler üzerinde mevcut olsa da). Bu, diyelim ki, dökme anlamına gelir ArrayList<String>için List<File>. Derleyici size uyarılar verecektir, ancak ArrayList<String>bir Objectreferansa atama ve sonra atama yaparsanız sizi de uyaracaktır List<String>. Sonuç olarak, jeneriklerle muhtemelen döküm yapmamalısınız, performans gereksiz veriler olmadan ve elbette geriye dönük uyumluluk olmadan daha iyi performans gösterir.

Bazı insanlar, genel argümanlara ( void fn(Set<String>)ve void fn(Set<File>)) dayanarak aşırı yüklenemeyeceğinden şikayet eder . Bunun yerine daha iyi yöntem adları kullanmanız gerekir. Aşırı yükleme statik, derleme zamanı sorunu olduğundan, bu aşırı yüklenmenin yeniden sertifikalandırma gerektirmeyeceğini unutmayın.

İlkel tipler jeneriklerle çalışmaz.

Joker karakterler ve sınırlar oldukça karmaşıktır. Çok faydalılar. Eğer Java değişmezliği ve söyleme-sorma arayüzlerini tercih ederse, kullanım tarafı jeneriklerinden ziyade beyan tarafı daha uygun olacaktır.


"Aşırı yükleme statik, derleme zamanı meselesi olduğundan, bu aşırı yüklemenin yeniden düzenleme gerektirmeyeceğini unutmayın." Öyle değil mi? Rehabilitasyon olmadan nasıl çalışır? JVM'nin hangi yöntemi kullanacağınızı, çalışma zamanında hangi yöntemi seçeceğinizi bilmesi gerekmez mi?
MatrixFrog

2
@ MatrixFrog Hayır. Dediğim gibi, aşırı yükleme bir derleme zamanı sorunudur. Derleyici, belirli bir aşırı yüklemeyi seçmek için ifadenin statik türünü kullanır, bu daha sonra sınıf dosyasına yazılan yöntemdir. Eğer Object cs = new char[] { 'H', 'i' }; System.out.println(cs);saçma sapan bastırıyorsanız. Türünü değiştirme csiçin char[]ve alacağınız Hi.
Tom Hawtin - tackline

10

Java jenerik emir, çünkü aşağıdakileri yapamazsınız:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

Eğer jenerik gerçekten jenerik sınıf parametreleri olsaydı olması gerektiği gibi çalışması için yukarıdaki koddaki her sınıfı kesmeniz gerekmez.


Her dersi kesmene gerek yok. Sadece uygun bir fabrika sınıfı eklemeniz ve AsyncAdapter'a enjekte etmeniz yeterlidir. (Ve temelde bu, jenerikler ile ilgili değil, ancak yapıcıların Java ile miras alınmaması gerçeğidir).
Peter Taylor

13
@PeterTaylor: Uygun bir fabrika sınıfı sadece tüm kazan parçalarını görüş alanı dışına çıkarır ve problemi çözmez. Şimdi her seferinde yeni bir örneğini başlatmak istediğimde Parserfabrika kodunu daha karmaşık hale getirmem gerekecek. Oysa eğer "jenerik" gerçekten jenerik ise, o zaman fabrikalara ve tasarım desenleri adına geçerli olan diğer bütün saçmalıklara gerek kalmayacaktı. Dinamik dillerde çalışmayı tercih etmemdeki birçok nedenden biri.
davidk01

2
Bazen bir kurucu adresini iletemediğiniz C ++ 'a benzer, şablonlar + sanalları kullanırken - bunun yerine bir fabrika işlevini kullanabilirsiniz.
Mark K Cowan,

Java bir yöntemin adından önce "korumalı" yazamadığınız için berbat mı? Yazık sana. Dinamik dillere bağlı kalın. Ve özellikle Haskell'e karşı dikkatli olun.
fdreger

5
  • hiçbir amaca hizmet etmeyen eksik genel parametreler için derleyici uyarıları, dili anlamsız derecede ayrıntılı kılar, örn. public String getName(Class<?> klazz){ return klazz.getName();}

  • Jenerikler dizilerle iyi oynamazlar

  • Kayıp tip bilgisi yansıma bir döküm ve koli bandı karışıklığına neden olur.


Kullanım HashMapyerine bir uyarı aldığımda sinirleniyorum HashMap<String, String>.
Michael K,

1
@Michael ama aslında gerekir jenerik ile kullanılabilir ...
alternatif

Dizi sorunu yeniden tanımlanabilirliğe gider. Açıklama için Java Generics kitabına (Timsah) bakın.
ncmathsadist

5

Bence diğer cevaplar bunu bir dereceye kadar söyledi, ama çok net değil. Jeneriklerle ilgili sorunlardan biri, yansıtma işlemi sırasında genel türleri kaybetmektir. Yani örneğin:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Ne yazık ki, döndürülen türler size arr'ın genel türlerinin String olduğunu söyleyemez. İnce bir fark, ama önemli. Çalışma zamanında arr oluşturulduğundan, genel türler çalışma zamanında silinir, böylece çözemezsiniz. Bazı belirtildiği ArrayList<Integer>gibi ArrayList<String>bir yansıma açısından aynı görünüyor .

Bu, Generics kullanıcısı için önemli olmayabilir, ancak diyelim ki, kullanıcının bir örneğin somut jenerik türlerini nasıl ilan ettiği hakkında süslü şeyler bulmak için yansıma kullanan bazı süslü çerçeve oluşturmak istediğimizi varsayalım.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Diyelim ki MySpecialObject, bu örnek için beyan ettiğimiz somut jenerik tür olduğundan, bir örnek fabrika yaratmak istedik . Peki Fabrika sınıfı, Java tarafından silindiği için bu örnek için açıklanan somut türü bulmak için kendisini sorgulayamaz.

.Net'in jeneriklerinde bunu yapabilirsiniz çünkü çalışma zamanında nesne, jenerik tiplerini bilir çünkü derleyici onu ikilide tamamlar. Silme işlemleri ile Java bunu yapamaz.


1

Jenerikler hakkında birkaç iyi şey söyleyebilirim , ama sorun bu değildi. Çalışma zamanında müsait olmadıklarından ve dizilerle çalışmadıklarından şikayet edebilirim ama bu belirtildi.

Büyük bir psikolojik sıkıntı: Bazen jeneriklerin çalışamayacağı durumlara giriyorum. (Diziler en basit örnek.) Ve ben jeneriklerin işi yapıp yapamayacağını ya da sadece aptal olup olmadığımı çözemiyorum. Bundan nefret ediyorum. Jenerikler gibi bir şey her zaman çalışması gerekir. Diğer her zaman ben, ben Java-dili kullanarak istediğini yapamaz biliyorum sorunun bende olduğunu ve ben sadece zorlamaya devam, ben orada sonunda alırsınız biliyorum. Jeneriklerle, çok ısrarcı kalırsam çok fazla zaman kaybedebilirim.

Ancak asıl sorun , jeneriklerin çok az kazanç için çok fazla karmaşıklık katmasıdır. En basit durumlarda, araba içeren bir listeye elma eklememi engelleyebilir. İnce. Ancak jenerik ürünler olmadan, bu hata ClassCastException'ı çok az zaman harcayarak çalışma zamanında gerçekten hızlı bir şekilde atabilir. İçinde bir çocuk koltuğu olan bir araba eklersem, listenin sadece bebek şempanze içeren çocuk koltukları olan araçlar için derleme zamanı uyarısına ihtiyacım var mı? Düz Nesne örnekleri listesi iyi bir fikir gibi görünmeye başlar.

Genel kod, çok fazla kelime ve karaktere sahip olabilir ve fazladan yer kaplar ve okumak için daha uzun sürebilir. İşe yaraması için bu ekstra kodu almak için çok zaman harcayabilirim. Olduğunda, kendimi zeki hissediyorum. Ayrıca kendi zamanımın birkaç saatini veya daha fazlasını boşa harcadım ve başkasının kodu çözebilecek birilerinin olup olmadığını merak etmem gerekiyor. Bakım yapmak için kendimden daha az akıllı olan ya da daha az zaman harcayan insanlara verebilmek istiyorum.

Öte yandan (orada biraz yaralandım ve biraz denge sağlamaya ihtiyacım var), basit koleksiyonları ve haritaları kullanarak ekler, ekler ve yazarken kontrol edilmek güzeldir ve genellikle çok fazla eklemez Kodun karmaşıklığı ( eğer başka biri koleksiyonu veya haritayı yazarsa ) . Java bu konuda C # dan iyidir. Görünüşe göre kullanmak istediğim C # koleksiyonlarından hiçbiri jeneriklerle ilgilenmiyor. (Koleksiyonlarda garip zevklerim olduğunu itiraf ediyorum.)


Güzel rant. Bununla birlikte, jenerik olmadan var olabilecek birçok kütüphane / çerçeve vardır, örneğin, Guice, Gson, Hibernate. Ve jenerikler, buna alışınca bir o kadar da zor değildir. Tip argümanlarını yazmak ve okumak gerçek bir PITA; burada val kullanabilirsiniz eğer yardımcı olur.
maaartinus

1
@ Maaartinus: Bunu bir süre önce yazdı. Ayrıca OP "sorunlar" istedi. Jenerik ilaçları son derece yararlı buluyorum ve onları çok seviyorum - ondan çok daha fazla. Ancak, eğer kendi koleksiyonunu yazıyoruz, onlar öğrenmek çok zordur. Ve koleksiyonunuzun türü çalışma zamanında belirlendiğinde kullanımları kaybolur - koleksiyonun size girişlerinin sınıfını söyleyen bir yöntemi olduğunda . Bu noktada jenerikler çalışmaz ve IDE'niz yüzlerce anlamsız uyarı üretecektir. Java programlamanın% 99,99'u buna dahil değildir. Ama ediyorum sadece bir vardı çok yukarıda yazdığım zaman bununla bela.
RalphChapin

Hem Java hem de C # geliştiricisi olarak neden Java koleksiyonlarını tercih edeceğinizi merak ediyorum.
Ivaylo Slavov

Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.Bu gerçekten, programın başlatılması ve söz konusu kod satırına çarpılması arasında ne kadar çalışma süresi olduğuna bağlı. Bir kullanıcı hatayı bildirirse ve yeniden üretmeniz birkaç dakika (veya en kötü senaryo, saat veya hatta günler) alırsa, bu derleme zamanı doğrulaması daha iyi ve daha iyi görünmeye başlar ...
Mason Wheeler
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.