C # ve Java… 'da Generics ve C ++' da Şablonlar arasındaki farklar nelerdir? [kapalı]


203

Çoğunlukla Java kullanıyorum ve jenerikler nispeten yenidir. Java'nın yanlış karar verdiğini veya .NET'in daha iyi uygulamaları vb.

Peki, jeneriklerde C ++, C #, Java arasındaki temel farklar nelerdir? Her birinin artıları / eksileri?

Yanıtlar:


364

Sesimi gürültüye ekleyeceğim ve işleri netleştirmek için bir bıçak alacağım:

C # Jenerikler böyle bir şey bildirmek için izin verir.

List<Person> foo = new List<Person>();

ve sonra derleyici Person, listede olmayan şeyleri koymanızı engeller .
Perde arkasında C # derleyicisi sadece List<Person>.NET dll dosyasına koyuyor , ancak çalışma zamanında JIT derleyicisi gider ve yeni bir kod kümesi oluşturur, sanki insanları içermek için özel bir liste sınıfı yazmışsınız gibi ListOfPerson.

Bunun yararı, onu gerçekten hızlı hale getirmesidir. Döküm ya da başka bir şey yoktur ve dll, bunun bir Listesi olduğu bilgisini içerdiğinden, Persondaha sonra yansıma kullanıldığında ona bakan diğer kodlar Personnesneler içerdiğini söyleyebilir (böylece intellisense vb.).

Bunun dezavantajı, eski C # 1.0 ve 1.1 kodunun (jenerikler eklemeden önce) bunları yeni anlamamasıdır List<something>, bu nedenle List, onlarla birlikte çalışmak için işleri manuel olarak düz eski haline dönüştürmeniz gerekir. C # 2.0 ikili kod geriye doğru uyumlu olmadığı için bu kadar büyük bir sorun değil. Bunun gerçekleşeceği tek zaman, eski C # 1.0 / 1.1 kodunu C # 2.0'a yükseltiyorsanız

Java Generics böyle bir şey bildirmenize izin verir.

ArrayList<Person> foo = new ArrayList<Person>();

Yüzeyde aynı görünüyor ve bir çeşit. Derleyici ayrıca Personlistede olmayan şeyleri koymanızı da önleyecektir .

Aradaki fark, perde arkasında olan şey. C # 'ın aksine, Java gitmez ve özel bir yapı oluşturmaz ListOfPerson- sadece ArrayListher zaman Java'da olan düz eski kullanır . Diziden bir şeyler çıkardığınızda, normal Person p = (Person)foo.get(1);döküm dansının hala yapılması gerekir. Derleyici tuşa basmanızı sağlıyor, ancak hız vuruşu / döküm her zamanki gibi hala gerçekleşiyor.
İnsanlar "Tip Erasure" dan bahsettiklerinde bundan bahsediyorlar. Derleyici, yayınları sizin için ekler ve daha sonra Personsadece bir liste olmaması anlamına gelir.Object

Bu yaklaşımın yararı, jenerikleri anlamayan eski kodların umrunda olması gerekmemesidir. Hala her zamankiyle aynı eski ile uğraşıyor ArrayList. Bu Java dünyasında daha önemlidir, çünkü Java 5'i jeneriklerle kullanarak kod derlemeyi ve microsoft'un kasıtlı olarak rahatsız etmemeye karar verdiği eski 1.4 veya önceki JVM'lerde çalışmasını istediler.

Dezavantajı, daha önce bahsettiğim hız hitidir ve ayrıca ListOfPersonsözde sınıf veya bunun gibi herhangi bir şey olmadığından .class dosyalarına, daha sonra bakılan koda (yansıma ile veya başka bir koleksiyondan çıkarırsanız) dönüştürüldüğünde Objectvb.), herhangi bir şekilde, yalnızca Personbaşka bir dizi listesi değil , yalnızca içeren bir liste olması gerektiğini söyleyemez.

C ++ Şablonları böyle bir şey beyan etmenizi sağlar

std::list<Person>* foo = new std::list<Person>();

C # ve Java jeneriklerine benziyor ve yapması gerektiğini düşündüğünüz şeyi yapacak, ancak sahne arkasında farklı şeyler oluyor.

Cava jenerikleri ile en yaygın olanı, pseudo-classessadece java gibi tür bilgilerini atmak yerine özel bir yapı oluşturmasıdır , ancak tamamen farklı bir balık su ısıtıcısıdır.

Hem C # hem de Java, sanal makineler için tasarlanmış çıktılar üretir. Personİçinde bir sınıfı olan bir kod yazarsanız , her iki durumda da bir Personsınıfla ilgili bazı bilgiler .dll veya .class dosyasına gider ve JVM / CLR bununla bir şeyler yapar.

C ++ ham x86 ikili kod üretir. Her şey bir nesne değildir ve bir Personsınıf hakkında bilmesi gereken temel bir sanal makine yoktur . Boks veya kutudan çıkarma yoktur ve işlevlerin sınıflara veya aslında hiçbir şeye ait olması gerekmez.

Bu nedenle, C ++ derleyicisi şablonlarla yapabileceklerinizle ilgili herhangi bir kısıtlama getirmez - temel olarak elle yazabileceğiniz herhangi bir kod, sizin için yazabileceğiniz şablonlar alabilirsiniz.
Bunun en açık örneği bir şeyler eklemektir:

C # ve Java'da, jenerik sistem bir sınıf için hangi yöntemlerin kullanılabilir olduğunu bilmeli ve bunu sanal makineye aktarmalıdır. Bunu söylemenin tek yolu ya gerçek sınıfı kodlamak ya da arabirimler kullanmaktır. Örneğin:

string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

Bu kod C # veya Java'da derlenmez, çünkü türün Taslında Name () adında bir yöntem sağladığını bilmez . Bunu söylemek zorundasınız - C #'da şöyle:

interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }

Ve sonra addNames'e ilettiğiniz şeylerin IHasName arabirimini uyguladığından emin olmalısınız. Java sözdizimi farklıdır ( <T extends IHasName>), ancak aynı sorunlardan muzdariptir.

Bu sorunun 'klasik' durumu, bunu yapan bir işlev yazmaya çalışıyor.

string addNames<T>( T first, T second ) { return first + second; }

Aslında bu kodu yazamazsınız, çünkü içindeki yöntemle bir arabirim bildirmenin bir yolu yoktur +. Kaybettin.

C ++ bu sorunların hiçbirinden muzdarip değildir. Derleyici, türlerin herhangi bir VM'ye geçmesini umursamaz - her iki nesneniz de .Name () işlevine sahipse derlenir. Eğer yapmazlarsa, olmayacak. Basit.

Yani, işte orada :-)


8
C # referans türleri için oluşturulan sözde aynı uygulamayı paylaşır, böylece tam ListOfPeople elde olmaz. Check out blogs.msdn.com/ericlippert/archive/2009/07/30/...
Piotr Czapla

4
Hayır, olabilir değil eşdeğer ilaç kullanımıyla Java 5 kodunu derlemek ve eski 1.4 VM'lerin yayınlanması (Güneş JDK bu uygulamıyor en azından. Bazı 3. parti araçları yapın.) Ne yapabilirsiniz 1,4 kavanozları gelen önceden derlenmiş kullanımıdır 1.5 / 1.6 kodu.
finnw

4
int addNames<T>( T first, T second ) { return first + second; }C # ile yazamadığınız ifadesine itiraz ediyorum . Genel tür, arabirim yerine bir sınıfla sınırlandırılabilir ve içindeki işleçle bir sınıf bildirmenin bir yolu vardır +.
Mashmagar

4
@AlexanderMalakhov amaç için deyimsel değil. Mesele C ++ deyimleri hakkında eğitmek değil, aynı kod parçasının her dil tarafından nasıl farklı şekilde ele alındığını göstermekti. Bu hedefe kodun daha farklı görünmesi daha zor olurdu
Orion Edwards

3
@phresnel Prensipte katılıyorum, ancak bu parçacığı deyimsel C ++ ile yazsaydım, C # / Java geliştiricileri için çok daha az anlaşılabilir olurdu ve bu nedenle (inanıyorum) farkı açıklamakta daha kötü bir iş yapmış olurdu. Buna katılmamaya karar verelim :-)
Orion Edwards

61

C ++ nadiren “jenerikler” terminolojisini kullanır. Bunun yerine, "şablonlar" sözcüğü kullanılır ve daha doğrudur. Şablonlar, genel bir tasarıma ulaşmak için bir tekniği açıklar .

C ++ şablonları, C # ve Java'nın iki ana nedenden dolayı uyguladığından çok farklıdır. İlk neden, C ++ şablonlarının yalnızca derleme zamanı türü bağımsız değişkenlerine değil, aynı zamanda derleme zamanı const değeri bağımsız değişkenlerine de izin vermesidir: şablonlar tamsayılar, hatta işlev imzaları olarak verilebilir. Bu, derleme zamanında oldukça korkak şeyler yapabileceğiniz anlamına gelir, örneğin hesaplamalar:

template <unsigned int N>
struct product {
    static unsigned int const VALUE = N * product<N - 1>::VALUE;
};

template <>
struct product<1> {
    static unsigned int const VALUE = 1;
};

// Usage:
unsigned int const p5 = product<5>::VALUE;

Bu kod ayrıca C ++ şablonlarının diğer ayırt edici özelliğini, yani şablon uzmanlığını kullanır. Kod product, bir değer bağımsız değişkeni olan bir sınıf şablonu tanımlar . Ayrıca, bağımsız değişken 1 olarak değerlendirildiğinde kullanılan bu şablon için bir uzmanlık tanımlar. Bu, şablon tanımları üzerinden bir özyineleme tanımlamama olanak tanır. Bunun ilk olarak Andrei Alexandrescu tarafından keşfedildiğine inanıyorum .

Şablon uzmanlığı C ++ için önemlidir çünkü veri yapılarında yapısal farklılıklara izin verir. Şablonlar bir bütün olarak, bir arabirimi türler arasında birleştirmenin bir yoludur. Bununla birlikte, bu istenmesine rağmen, uygulama içinde tüm tiplere eşit muamele edilemez. C ++ şablonları bunu dikkate alır. Bu, OOP'nin sanal yöntemlerin geçersiz kılınmasıyla arayüz ve uygulama arasında yaptığı farkın aynısıdır.

C ++ şablonları algoritmik programlama paradigması için gereklidir. Örneğin, kaplar için hemen hemen tüm algoritmalar kap türünü şablon türü olarak kabul eden ve eşit olarak ele alan işlevler olarak tanımlanır. Aslında, bu doğru değil: C ++ kaplar üzerinde değil, kabın başına ve arkasına işaret eden iki yineleyici tarafından tanımlanan aralıklarda çalışır . Böylece, tüm içerik yineleyiciler tarafından sınırlandırılmıştır: begin <= elements <end.

Kaplar yerine yineleyiciler kullanmak yararlıdır çünkü bir kabın bütünü yerine parçaları üzerinde çalışmasına izin verir.

C ++ 'nın diğer bir ayırt edici özelliği, sınıf şablonları için kısmi uzmanlaşma olasılığıdır . Bu biraz Haskell ve diğer fonksiyonel dillerdeki argümanlarda desen eşleşmesi ile ilgilidir. Örneğin, elemanları depolayan bir sınıfı ele alalım:

template <typename T>
class Store {  }; // (1)

Bu, herhangi bir eleman türü için geçerlidir. Ancak, özel bir numara uygulayarak, işaretçileri diğer türlerden daha etkili bir şekilde depolayabileceğimizi varsayalım. Bunu tüm işaretçi türleri için kısmen uzmanlaştırarak yapabiliriz :

template <typename T>
class Store<T*> {  }; // (2)

Şimdi, bir tür için bir kap şablonu her örneklediğimizde, uygun tanım kullanılır:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.

Bazen .net'teki jenerikler özelliğinin anahtarların yanı sıra türlerin kullanılmasına izin vermesini diledim. Değer türü diziler Çerçevenin bir parçasıysa (sabit boyutlu dizileri yapılar içine yerleştiren eski API'larla etkileşim kurma ihtiyacı göz önüne alındığında, bir şekilde, şaşırmadım), birkaç bağımsız öğe ve ardından boyutu genel bir parametre olan bir değer türü dizi içeren bir sınıf. Olduğu gibi, en yakın olan tek tek öğeleri tutan ve daha sonra diziyi tutan ayrı bir nesneye referans tutan bir sınıf nesnesine sahip olmaktır.
supercat

@supercat Eski API ile etkileşime girerseniz fikir marşaling kullanmaktır (özniteliklerle açıklanabilir). CLR'nin sabit boyutlu dizileri zaten yok, bu nedenle tür olmayan şablon bağımsız değişkenlerine sahip olmak burada yardımcı olmaz.
Konrad Rudolph

Şaşırtıcı bulduğum şey, sabit boyutlu değer türü dizilere sahip olmanın zor olmaması gerektiği ve birçok veri türünün değere göre referansla sıralanmasına izin vereceğidir. Mareşal değeri başka bir şekilde ele alınamayan durumlarda yararlı olsa da, marshal-by-ref'nin kullanılabilir olduğu hemen hemen tüm durumlarda üstün olduğunu kabul edeceğim, bu nedenle bu tür durumların sabit boyutlu diziler yararlı bir özellik gibi görünüyordu.
supercat

BTW, tip dışı jenerik parametrelerin yararlı olacağı bir diğer durum, boyutlandırılmış miktarları temsil eden veri tipleriyle olacaktır. Miktarları temsil eden örneklerin içinde boyutsal bilgi içerebilir, ancak bir tür içinde bu tür bilgilere sahip olmak, bir koleksiyonun belirli bir boyutlandırılmış birimi temsil eden nesneleri tutması gerektiğini belirtmesine izin verir.
Supercat


18

Farklılıkların ne olduğu konusunda zaten çok iyi cevaplar var, bu yüzden biraz farklı bir perspektif vereyim ve nedenini ekleyeyim .

Daha önce açıklandığı gibi, ana fark tip silme , yani Java derleyicisinin genel türleri silmesi ve oluşturulan bayt kodunda bulunmamasıdır. Ancak soru şu: neden bunu kimse yapsın ki? Mantıklı değil! Yoksa öyle mi?

Peki, alternatif nedir? Eğer dilde jeneriği uygulamak yoksa, nerede yapmak bunları uygulamak? Ve cevap: Sanal Makinede. Geriye dönük uyumluluğu bozar.

Tür silme ise, genel istemcileri genel olmayan kitaplıklarla karıştırmanıza olanak tanır. Başka bir deyişle: Java 5'de derlenen kod yine de Java 1.4'e dağıtılabilir.

Ancak Microsoft, jenerikler için geriye dönük uyumluluğu bozmaya karar verdi. Yıllardan bu .NET Jenerik Java Generics "daha iyi" neden.

Tabii ki, Sun aptal ya da korkak değil. "Dışarı çıkmalarının" nedeni Java'nın jenerikleri tanıttıklarında .NET'ten çok daha eski ve yaygın olmasıydı. (Kabaca aynı anda her iki dünyada da tanıtıldı.) Geriye dönük uyumluluğu bozmak büyük bir acı olurdu.

Yine bir başka deyişle: Java, Jenerik bir parçası olan dil (bunlar geçerli demektir sadece diğer dillere değil, Java), .NET içinde parçası oldukları Virtual Machine onlar için geçerli olan araçlar ( hepsi , diller değil sadece C # ve Visual Basic.NET).

Bunu LINQ, lambda ifadeleri, yerel değişken türü çıkarımı, anonim türler ve ifade ağaçları gibi .NET özellikleriyle karşılaştırın: bunların hepsi dil özellikleridir. Bu yüzden VB.NET ve C # arasında küçük farklılıklar vardır: bu özellikler VM'nin bir parçası olsaydı, tüm dillerde aynı olurdu . Ancak CLR değişmedi: .NET 3.5 SP1'de hala .NET 2.0'daki ile aynı. .NET 3.5 derleyicisiyle LINQ kullanan bir C # programı derleyebilir ve .NET 3.5 kitaplıklarını kullanmamanız koşuluyla .NET 2.0 üzerinde çalıştırabilirsiniz. Yani olur değil jenerik ve .NET 1.1 ile çalışır, ancak bunun olacağını Java ve Java 1.4 ile çalışır.


3
LINQ, öncelikle bir kütüphane özelliğidir (C # ve VB, yanında sözdizimi şekeri de eklemiştir). 2.0 CLR'yi hedefleyen herhangi bir dil, yalnızca System.Core derlemesini yükleyerek LINQ'dan tam olarak yararlanabilir.
Richard Berg

Evet, üzgünüm, daha net olmalıydım. LINQ. Ben monadic standart sorgu operatörleri, LINQ uzantısı yöntemleri veya IQueryable arabirimi değil, sorgu sözdizimi atıfta. Açıkçası, bunları herhangi bir .NET dilinden kullanabilirsiniz.
Jörg W Mittag

1
Java için başka bir seçenek düşünüyorum. Oracle bile geriye dönük uyumluluğu kırmak istemese de, tür bilgilerinin silinmesini önlemek için bazı derleyici hileleri yapabilirler. Örneğin, ArrayList<T>(gizli) statik Class<T>alana sahip, dahili olarak adlandırılmış yeni bir tür olarak yayılabilir . Genel lib'in yeni sürümü 1.5+ bayt kodla dağıtıldığı sürece 1.4-JVM'lerde çalışabilecektir.
Dünya Motoru

14

Önceki gönderimin takibi.

Şablonlar, kullanılan IDE'ye bakılmaksızın, C ++ 'nın akıllıca başarısız olmasının ana nedenlerinden biridir. Şablon uzmanlığı nedeniyle, IDE belirli bir üyenin olup olmadığından asla emin olamaz. Düşünmek:

template <typename T>
struct X {
    void foo() { }
};

template <>
struct X<int> { };

typedef int my_int_type;

X<my_int_type> a;
a.|

Şimdi, imleç belirtilen konumda ve IDE'nin o noktada üyelerin asahip olup olmadığını söylemesi çok zor . Diğer diller için ayrıştırma basittir, ancak C ++ için önceden biraz değerlendirme yapılması gerekir.

Daha da kötüleşiyor. my_int_typeSınıf şablonunda da tanımlanmış olsaydı ne olurdu ? Şimdi türü başka bir tür argümanına bağlı olacaktır. Ve burada, derleyiciler bile başarısız oluyor.

template <typename T>
struct Y {
    typedef T my_type;
};

X<Y<int>::my_type> b;

Biraz düşündükten sonra, bir programcı bu kodun yukarıdakiyle aynı olduğu sonucuna varır: bu nedenle Y<int>::my_typeçözülür int, bu nedenle baynı tür olmalıdır a, değil mi?

Yanlış. Derleyicinin bu ifadeyi çözmeye çalıştığı noktada, aslında Y<int>::my_typehenüz bilmiyor ! Bu nedenle, bunun bir tür olduğunu bilmiyor. Başka bir şey olabilir, örneğin üye işlevi veya alan. Bu, mevcut davada olmasa da belirsizlikler doğurabilir, bu nedenle derleyici başarısız olur. Bir tür adına atıfta bulunduğumuzu açıkça belirtmeliyiz:

X<typename Y<int>::my_type> b;

Şimdi, kod derleniyor. Bu durumdan belirsizliklerin nasıl ortaya çıktığını görmek için aşağıdaki kodu göz önünde bulundurun:

Y<int>::my_type(123);

Bu kod deyimi mükemmel şekilde geçerlidir ve C ++ 'ya işlev çağrısını yürütmesini söyler Y<int>::my_type. Ancak, my_typebir işlev değil, bir tür ise, bu ifade hala geçerli olur ve genellikle bir yapıcı çağırma olan özel bir döküm (işlev stili döküm) gerçekleştirir. Derleyici hangisini kastettiğimizi söyleyemez, bu yüzden burada anlam ayrılmak zorundayız.


2
Oldukça katılıyorum. Yine de biraz umut var. Otomatik tamamlama sistemi ve C ++ derleyicisi çok yakın etkileşim içinde olmalıdır. Visual Studio'nun asla böyle bir özelliği olmayacağından eminim, ancak Eclipse / CDT'de veya GCC'ye dayalı başka bir IDE'de bir şeyler olabilir. UMUT ! :)
Benoît

6

Java ve C #, ilk dil sürümlerinden sonra jenerikleri tanıttı. Bununla birlikte, jenerikler tanıtıldığında çekirdek kütüphanelerin nasıl değiştiği konusunda farklılıklar vardır. C # 'ın jenerikleri sadece derleyici büyüsü değildir ve bu nedenle geriye dönük uyumluluğu bozmadan mevcut kütüphane sınıflarını doğrulamak mümkün olmamıştır .

Örneğin, Java mevcut Koleksiyonları Çerçeve edildi tamamen genericised . Java, koleksiyon sınıflarının hem genel hem de eski, genel olmayan bir sürümüne sahip değildir. Bazı açılardan bu çok daha temiz - C # 'da bir koleksiyon kullanmanız gerekiyorsa, genel olmayan versiyona gitmek için gerçekten çok az neden var, ancak bu eski sınıflar yerinde kalır ve manzarayı karıştırır.

Bir diğer önemli fark Java ve C # 'daki Enum sınıflarıdır. Java'nın Enum'u bu şekilde kıvrımlı görünümlü bir tanıma sahiptir:

//  java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

(Angelika Langer'ın bunun neden böyle olduğuna dair çok açık bir açıklamasına bakın . Temel olarak, Java, bir dizeden Enum değerine tipe güvenli erişim verebileceği anlamına gelir:

//  Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");

Bunu C # sürümüyle karşılaştırın:

//  Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");

Enum, jenerikler dile tanıtılmadan önce C #'da zaten mevcut olduğundan, tanım mevcut kodu bozmadan değiştirilemez. Yani, koleksiyonlar gibi, bu eski durumdaki çekirdek kütüphanelerde kalır.


C # 'ın jenerikleri bile sadece derleyici büyüsü değildir, derleyici mevcut kütüphaneyi doğrulamak için daha fazla büyü yapabilir. Onlar yeniden adlandırmak gerekir hiçbir neden yoktur ArrayListiçin List<T>ve yeni bir ad koydu. Gerçek şu ki, ArrayList<T>IL kodunda farklı bir derleyici tarafından üretilen sınıf adı olacağından kaynak kodunda bir sınıf varsa , bu nedenle herhangi bir ad çakışması olamaz.
Dünya Motoru

4

11 ay geç, ama bu soru bazı Java Wildcard şeyler için hazır olduğunu düşünüyorum.

Bu, Java'nın sözdizimsel bir özelliğidir. Bir yönteminiz olduğunu varsayalım:

public <T> void Foo(Collection<T> thing)

Ve varsayalım ki yöntem gövdesindeki T tipine bakmanıza gerek yok. T adını söylüyorsunuz ve sonra sadece bir kez kullanıyorsunuz, neden bunun için bir isim düşünmelisiniz? Bunun yerine şunları yazabilirsiniz:

public void Foo(Collection<?> thing)

Soru işareti derleyiciden, o noktada yalnızca bir kez görünmesi gereken normal bir adlandırılmış tür parametresi bildirdiğinizi iddia etmesini ister.

Joker karakterlerle, adlandırılmış bir type parametresiyle de yapamayacağınız hiçbir şey yoktur (bu şeyler her zaman C ++ ve C # 'da nasıl yapılır).


2
11 ay daha geç ... Java joker karakterleriyle adlandırılmış tür parametreleriyle yapamayacağınız şeyler var. Bunu Java'da yapabilirsiniz: class Foo<T extends List<?>>ve kullanın Foo<StringList>ancak C # 'da bu ekstra tür parametresini eklemeniz gerekir: class Foo<T, T2> where T : IList<T2>ve clunky kullanın Foo<StringList, String>.
R. Martinho Fernandes



1

C ++ şablonları, derleme zamanında değerlendirildikleri ve uzmanlaşmayı destekledikleri için C # ve Java muadillerinden çok daha güçlüdür. Bu, Şablon Meta-Programlamaya izin verir ve C ++ derleyicisini bir Turing makinesine eşdeğer hale getirir (yani derleme işlemi sırasında bir Turing makinesiyle hesaplanabilir herhangi bir şeyi hesaplayabilirsiniz).


1

Java'da, jenerikler yalnızca derleyici düzeyindedir, bu nedenle şunları elde edersiniz:

a = new ArrayList<String>()
a.getClass() => ArrayList

'A' türünün dize listesi değil, bir dizi listesi olduğunu unutmayın. Yani bir muz listesinin türü bir maymun listesine eşit olacaktır.

Tabiri caizse.


1

Görünüşe göre, diğer çok ilginç tekliflerin yanı sıra, jenerikleri geliştirmek ve geriye dönük uyumluluğu kırmakla ilgili bir tane var:

Şu anda, jenerikler silme kullanılarak uygulanmaktadır, yani jenerik tip bilgisi çalışma zamanında mevcut değildir, bu da bir tür kodun yazılmasını zorlaştırır. Jenerikler, eski jenerik olmayan kodlarla geriye dönük uyumluluğu desteklemek için bu şekilde uygulandı. Birleştirilmiş jenerikler, çalışma sırasında genel tür bilgisini kullanılabilir hale getirir ve eski jenerik olmayan kodu bozar. Ancak Neal Gafter, geriye dönük uyumluluğu bozmamak için türleri yalnızca belirtildiyse yeniden kullanılabilir hale getirmeyi önerdi.

En Java 7 Teklif hakkında Alex Miller'in makalesinde


0

Not: Yorum yapmak için yeterli noktam yok, bu yüzden bunu uygun cevaba bir yorum olarak taşımaktan çekinmeyin.

Nereden geldiğini asla anlamadığım popüler inanışın aksine, .net geriye dönük uyumluluğu bozmadan gerçek jenerikleri uyguladı ve bunun için açık bir çaba harcadılar. Genel olmayan .net 1.0 kodunuzu, yalnızca .net 2.0'da kullanılacak jeneriklere dönüştürmeniz gerekmez. Hem genel hem de genel olmayan listeler, 4.0'a kadar bile .Net framework 2.0'da, geriye dönük uyumluluk nedeninden başka hiçbir şey olmadan hala kullanılabilir. Bu nedenle, hala genel olmayan ArrayList kullanan eski kodlar çalışmaya devam eder ve önceki ile aynı ArrayList sınıfını kullanır. Geri kod uyumluluğu her zaman 1.0'dan bugüne kadar korunur ... Yani .net 4.0'da bile, bunu seçerseniz, hala 1.0 BCL'den herhangi bir jenerik olmayan sınıfı kullanma seçeneğini seçmeniz gerekir.

Bu yüzden java'nın gerçek jenerikleri desteklemek için geriye dönük uyumluluğu kırması gerektiğini düşünmüyorum.


Bu insanların konuştuğu türden geriye dönük uyumluluk değildir. Fikir için geriye dönük uyumluluk olduğu çalışma zamanı : Kod .NET 2.0 eşdeğer ilaç kullanımıyla yazılı olamaz .NET framework / CLR eski sürümlerinde çalıştırılabilir. Benzer şekilde, Java "gerçek" jenerikleri tanıtsaydı, daha yeni Java kodu eski JVM'lerde çalışamazdı (çünkü bayt kodundaki değişiklikleri kırmayı gerektirir).
tzaman

Bu .net, jenerikler değil. Belirli CLR sürümünü hedeflemek için her zaman yeniden derleme gerekir. Bayt kodu uyumluluğu var, kod uyumluluğu var. Ve ayrıca, özellikle yeni Liste'yi kullanmak için eski Listeyi kullanan eski kodu dönüştürme ihtiyacıyla ilgili olarak, hiç de doğru olmayan bir şekilde yanıt veriyordum.
Sheepy

1
Bence insanlar ileri uyumluluktan bahsediyorlar . Yani .net 2.0 kodu 1.1 çalışma zamanında çalışacak çünkü 1.1 çalışma zamanı 2.0 "sözde sınıf" hakkında hiçbir şey bilmiyor. O zaman "java ileri uyumluluğu korumak istedikleri için gerçek jenerik uygulamaz" olmamalı mı? (geriye değil)
Sheepy

Uyumluluk sorunları anlaşılır. Sorunun Java'ya "gerçek" jenerikler eklemenin, Java'nın eski sürümlerini kullanan herhangi bir programı etkileyeceğine inanmıyorum, bunun yerine "yeni geliştirilmiş" jenerikleri kullanan kodun bu tür nesneleri eski kodla alıp vermekte zorlanacağını düşünüyorum. yeni türler hakkında hiçbir şey bilmiyordu. Örneğin, bir programın ArrayList<Foo>bir ArrayListörneğiyle doldurması gereken daha eski bir yönteme geçmek istediğini varsayalım Foo. Eğer bir ArrayList<foo>değil ise ArrayList, bu nasıl çalışır?
supercat
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.