Jenerikler ve Tip Silme


10

Java'daki jenerikler tür silme kullanılarak uygulanır. JLS , ilhamın geriye dönük uyumluluk olduğunu söylüyor. Öte yandan C # jenerikleri yeniden kullanılabilir.

Teorik olarak Generics'i "silme" veya "yeniden kullanılabilir" olarak kullanmanın avantajları ve dezavantajları nelerdir?

Java bir şeyde eksik mi?

Yanıtlar:


8

Motivasyon (geriye dönük uyumluluk) hem bir avantaj hem de dezavantajdır. Dezavantajdır çünkü hepimiz yeniden kullanılabilir tiplere sahip olmayı tercih ederiz, ancak ödeme fiyatı yüksekti. C # 'daki tasarım seçeneklerini düşünün. Yeniden kullanılabilir türleri var, ancak şimdi yinelenen API'leri var. Bu nedenle, her parametreli sınıf için yinelenen API'lere sahip olduğumuz bir Java API'sini hayal edin. Şimdi, kendinizi eski sınıflardan yeni genel sınıflara binlerce kod satırı taşıdığınızı hayal edin. Şimdi, kim yinelenen API'leri bir dezavantaj olarak görmez ki? Ama hey, yeniden kullanılabilir türleri var!

Yani, temel motivasyon "devrim değil evrim" idi. Ve mantıklı olarak, her kararın ödünleşimi vardır.

Bahsedilen diğer dezavantajların yanı sıra, tür silme işleminin derleme zamanında akıl yürütmenin zor olabileceği gerçeğini de ekleyebiliriz, çünkü belirli türlerin kaldırılacağı açık değildir ve bu çok garip ve bulmak zor hatalara yol açar.

Köprü yöntemlerinin (ikili uyumluluğu korumak için sözdizimsel olarak üretilen bu yöntemler) varlığı dezavantaj olarak görülebilir. Bunlar, bir önceki paragrafta bahsettiğim hataların nedenlerinden biri olabilir.

Büyük dezavantajlar, genel tipler için birden fazla sınıf değil, tek bir sınıfın olduğu zaten açık olan gerçeğinden kaynaklanmaktadır. Başka bir örnek olarak, aynı genel sınıfa sahip bir yöntemin aşırı yüklenmesinin Java'da başarısız olduğunu düşünün:

public void doSomething(List<One>);
public void doSomething(List<Two>);

Yeniden kullanılabilir türlerin (en azından C # 'de) dezavantajı olarak görülebilecek bir şey, kod patlamasına neden olmalarıdır . Mesela List<int>bir sınıftır ve List<double>bu gibi başka tamamen farklı List<string>bir List<MyType>. Bu nedenle sınıflar çalışma zamanında tanımlanmalı, sınıfların patlamasına ve üretilirken değerli kaynakların tüketilmesine neden olmalıdır.

new T()Başka bir cevapta bahsedilen Java'da bir tanımlamanın mümkün olmadığı gerçeği ile ilgili olarak , bunun sadece tür silme meselesi olmadığını düşünmek de ilginçtir. Ayrıca, varsayılan bir kurucunun varlığını gerektirir, bu nedenle C # bunun için "yeni bir kısıtlama" gerektirir. (Bkz . Java'da neden yeni T () mümkün değil , Alex Buckley).


3
Ben CLR, referans türü T s Class<T>için, tüm Ts için kodun yalnızca bir kopyasını almak söyleyerek doğru olduğunu düşünüyorum ; artı gerçekte kullanılan her bir değer türü için ek bir kopya T.
AakashM

@AakashM: Doğru, genel başına tek bir Referans Türü sınıfı vardır, ancak her Değer Türü kendi sürümünü oluşturur. Bunun nedeni, türe göre özel depolama alanı sağlamasıdır, tüm referanslar aynı depolamayı alır, ancak değer türleri her tür için farklı depolama alanı alır.
Kasım'da Guvante

6

Silme türünün dezavantajı, çalışma zamanında jenerik türünü bilmemenizdir. Bu, onlara yansıma uygulayamayacağınız ve çalışma zamanında başlatamadığınız anlamına gelir.

Java'da böyle bir şey yapamazsınız:

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

Bunun için bir geçici çözüm vardır, ancak daha fazla kod gerektirir. Bahsettiğiniz gibi avantaj, geriye dönük uyumluluktur.


3

Silinen jeneriklerin bir başka avantajı, JVM'ye derleyen farklı dillerin jenerikler için farklı stratejiler kullanmasıdır, örneğin Scala'nın tanımlama sitesi ile Java'nın kullanım sitesi kovaryansına. Ayrıca Scala'nın yüksek tür jeneriklerini .Net'in birleştirilmiş türlerini desteklemek daha zor olurdu, çünkü esasen Scala'daki. Scala, C # 'ın uyumsuz yeniden yapılandırma formatını göz ardı etti. JVM'de jenerikleri birleştirmiş olsaydık, büyük olasılıkla bu birleşmiş jenerikler Scala hakkında gerçekten sevdiğimiz özellikler için uygun olmazdı ve yetersiz bir şeyle sıkışırdık. Alıntı Ola Bini blog'undaki ,

Tüm bunların anlamı, JVM'ye birleşik jenerikler eklemek istiyorsanız, uygulamanın hem kendi jenerik sürümlerinde yenilik yapmak isteyen tüm statik dilleri hem de Java kütüphaneleriyle iyi bir uygulama ve hoş bir arayüz oluşturma. Çünkü bu kriterleri karşılamayan birleşik jenerikler eklerseniz, yeniliği bastırır ve JVM'yi çok dilli bir sanal makine olarak kullanmayı daha zor hale getirirsiniz.

Şahsen TypeTag, Scala'da aşırı yüklenmiş yöntemler için çelişen bir bağlamın bir dezavantaj olarak kullanılmasının gerekliliğini düşünmüyorum , çünkü bu, yeniden yapılandırmanın genel yükünü ve esnekliğini bir dilden (tüm program ve tüm olası diller) dil başına bir kullanım alanına taşır. sorun sadece daha az sıklıkta görülür.



Silinen jenerikleri tercih etmenin bir başka nedeni, bağımlılıkların İfade Sorununun çözümüne saygı duyacak şekilde enjekte edilmesi gerektiğidir. Belirli tür durumları test ediyorsanız veya genel işlevinizde bir fabrika kodlaması yapıyorsanız, genişletilebilirlik yanlış yapıyorsunuz demektir.
Shelby Moore III
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.