Java: sınırlı joker karakterler veya sınırlı tür parametresi?


83

Son zamanlarda şu makaleyi okudum: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

Sorum şu, bunun gibi bir yöntem oluşturmak yerine:

public void drawAll(List<? extends Shape> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

Bunun gibi bir yöntem oluşturabilirim ve iyi çalışıyor

public <T extends Shape> void drawAll(List<T> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

Hangi yolu kullanmalıyım? Bu durumda joker karakter yararlı mı?


? kısa bir gösterimdir; dahili olarak derleyici bunu yine de bir tür parametresi ile değiştirir; derleyici hatası olduğunda,? yerine vekil tür parametresini görürsünüz.
reddedilemez

9
düzeltme: önceki yorumum YANLIŞ. joker karakter düşündüğümden daha karmaşık.
irreputable

@irreputable aslında daha karmaşık olsa da, onu bir tür parametresi ile değiştirme konusundaki düşünceniz tamamen geçerlidir; sadece ilan edemeyeceğin bir şey.
Eugene

sadece bu iki yönteme oldukları gibi bakarsak - hiçbir fark yoktur ve sadece bir stil meselesi; aksi takdirde (daha fazla genel parametreler eklerseniz), işler değişecek
Eugene

Yanıtlar:


134

Ne yapmanız gerektiğine bağlı. Bunun gibi bir şey yapmak istiyorsanız, sınırlı tür parametresini kullanmanız gerekir:

public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
    if (shape.isPretty()) {
       shapes.add(shape);
    }
}

Burada bir List<T> shapesve bir var T shape, bu nedenle güvenle yapabiliriz shapes.add(shape). Eğer ilan edilmişse List<? extends Shape>, güvenli bir şekilde YAPAMAZSINIZadd (çünkü bir List<Square>ve a'ya sahip olabilirsiniz Circle).

Dolayısıyla, sınırlı bir tür parametresine bir ad vererek, onu jenerik yöntemimizde başka bir yerde kullanma seçeneğine sahibiz. Elbette bu bilgiler her zaman gerekli değildir, bu nedenle tür hakkında çok fazla bilgi sahibi olmanız gerekmiyorsa (örneğin sizin drawAll), o zaman sadece joker karakter yeterlidir.

Sınırlı tür parametresine tekrar başvurmasanız bile, birden çok sınırınız varsa sınırlı bir tür parametresi yine de gereklidir. İşte Angelika Langer'in Java Generics SSS'lerinden bir alıntı

Joker karakter sınırı ile tür parametresi sınırı arasındaki fark nedir?

Bir joker karakterin yalnızca bir sınırı olabilirken, bir tür parametresinin birkaç sınırı olabilir. Bir joker karakter, alt veya üst sınıra sahip olabilirken, tür parametresi için alt sınır diye bir şey yoktur.

Joker karakter sınırları ve tür parametresi sınırları genellikle karıştırılır, çünkü her ikisi de sınır olarak adlandırılır ve kısmen benzer sözdizimine sahiptir. […]

Sözdizimi :

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType

Bir joker karakterin alt veya üst sınırı olmak üzere yalnızca bir sınırı olabilir. Joker karakter sınırları listesine izin verilmez.

Constrast'ta bir tür parametresinin birkaç sınırı olabilir, ancak bir tür parametresi için alt sınır diye bir şey yoktur.

Etkili Java 2. Sürüm, Madde 28'den alıntılar : API esnekliğini artırmak için sınırlı joker karakterler kullanın :

Maksimum esneklik için, üreticileri veya tüketicileri temsil eden girdi parametrelerinde joker türler kullanın. […] PECS, üretici- extends, tüketici- super[…]

Döndürme türleri olarak joker karakter türlerini kullanmayın . Kullanıcılarınız için ek esneklik sağlamak yerine, onları istemci kodunda joker karakter türleri kullanmaya zorlar. Uygun şekilde kullanıldığında, joker karakter türleri bir sınıfın kullanıcıları için neredeyse görünmezdir. Yöntemlerin kabul etmeleri gereken parametreleri kabul etmelerine ve reddetmeleri gerekenleri reddetmelerine neden olurlar. Sınıfın kullanıcısı joker türleri hakkında düşünmek zorunda kalırsa, muhtemelen sınıfın API'sinde bir sorun vardır .

PECS ilkesini uygulayarak şimdi addIfPrettyörneğimize geri dönebilir ve aşağıdakileri yazarak onu daha esnek hale getirebiliriz:

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

Şimdi edebilir addIfPretty, bir söylemek Circlebir etmek, List<Object>. Açıkçası bu tür güvenlidir, ancak orijinal beyanımız buna izin verecek kadar esnek değildi.

İlgili sorular


Özet

  • Sınırlı tür parametreleri / joker karakterler kullanın, bunlar API'nizin esnekliğini artırır
  • Tür birkaç parametre gerektiriyorsa, sınırlı tür parametresi kullanmaktan başka seçeneğiniz yoktur.
  • tür bir alt sınır gerektiriyorsa, sınırlı joker karakter kullanmaktan başka seçeneğiniz yoktur
  • "Üreticilerin" üst sınırı, "tüketicilerin" daha düşük sınırı vardır
  • Dönüş türlerinde joker karakter kullanmayın

Çok teşekkür ederim, açıklamanız çok açık ve öğretici
Tony Le

1
@Tony: Kitapta, sınır olmadığında joker karakter ve tür parametresi arasında nasıl seçim yapılacağına dair kısa bir tartışma var. Esasen, tür parametresi bir yöntem bildiriminde yalnızca bir kez görünüyorsa, joker karakter kullanın. Ayrıca reverse(List<?>)JLS java.sun.com/docs/books/jls/third_edition/html/…
polygenelubricants

Aşağıdaki kod için UnsupportedOperationException alıyorum, <code> public static <T extends Number> void add (List <? Super T> list, T num) {list.add (num); } </code>
Samra

1
Ayrıca - ?bir yöntem parametresi olarak geçemezsiniz , doWork(? type)çünkü o zaman bu parametreyi yöntemde kullanamazsınız. Tür parametresini kullanmanız gerekir.
Tomasz Mularczyk

1
@VivekVardhan joker karakter kullanarak böyle bir yöntem oluşturamazsınız, mesele bu. Hangisinin kullanılacağına karar verirken, bu açıklayan en iyi kaynaklardan biridir.
Eugene

6

Örneğinizde, bu türü başka hiçbir yerde kullanmadığınız için, gerçekten T kullanmanıza gerek yok.

Ama şöyle bir şey yaptıysanız:

public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
    T s = shapes.get(0);
    s.draw(this);
    return s;
}

veya listedeki tür parametresini başka bir tür parametresiyle eşleştirmek istiyorsanız, poligen yağlayıcıların söylediği gibi:

public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) {
    List<T> mergedList = new ArrayList<T>();
    mergedList.addAll(shapes1);
    mergedList.addAll(shapes2);
    for (Shape s: mergedList) {
        s.draw(this);
    }
}

İlk örnekte, biraz daha fazla tür güvenliği elde edersiniz ve ardından sadece Shape döndürürsünüz, çünkü daha sonra sonucu Shape'in altını alabilecek bir işleve aktarabilirsiniz. Örneğin List<Square>, benim yöntemime bir geçebilir ve ardından elde edilen Kareyi yalnızca Kareler alan bir yönteme iletebilirsiniz. "?" Kullandıysanız sonuçta elde edilen Şekli Kareye çevirmeniz gerekir ki bu tür güvenli olmayacaktır.

İkinci örnekte, her iki listenin de aynı tür parametresine sahip olmasını sağlıyorsunuz (her biri '?' Farklı olduğu için '?' İle yapamazsınız), böylece her ikisinden de tüm öğeleri içeren bir liste oluşturabilirsiniz .


Shape s = ...Olması gerektiğine inanıyorum T s = ..., aksi takdirde return s;derlenmemeli.
polygenelubricants

1

Aşağıdaki 2 SinglyLinkQueue'yu birleştirmek istediğimiz James Gosling'in 4. baskısının Java Programlama örneğini düşünün:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

Yukarıdaki yöntemlerin her ikisi de aynı işlevselliğe sahiptir. Peki hangisi tercih edilir? Cevap 2. Yazarın kendi sözleriyle:

"Genel kural, mümkün olduğunda joker karakterler kullanmaktır çünkü joker karakterli kod genellikle birden çok tür parametresi olan koddan daha okunabilirdir. Bir tür değişkenine ihtiyacınız olup olmadığına karar verirken, bu tür değişkeninin iki veya daha fazla parametreyi ilişkilendirmek için kullanılıp kullanılmadığını kendinize sorun, veya bir parametre türünü dönüş türüyle ilişkilendirmek için. Cevap hayır ise, bir joker karakter yeterli olmalıdır. "

Not: Kitapta yalnızca ikinci yöntem verilmiştir ve parametre adı 'T' yerine S'dir. İlk yöntem kitapta yok.


1

Anladığım kadarıyla joker karakter, bir tür parametresinin gerekli olmadığı durumlarda (örneğin, birkaç yerde referans verildiği için veya diğer yanıtlarda ayrıntılı olarak belirtildiği gibi birden çok sınır gerektiği için) daha kısa kodlara izin veriyor.

Bağlantıda, bu yönde ipucu veren aşağıdaki ifadeleri okuduğumu ("Genel Yöntemler" altında) belirtiyorsunuz:

Genel yöntemler, tür parametrelerinin bir veya daha fazla argümanın türleri arasında bir yönteme ve / veya dönüş türüne bağımlılıkları ifade etmek için kullanılmasına izin verir. Böyle bir bağımlılık yoksa jenerik bir yöntem kullanılmamalıdır.

[...]

Joker karakterlerin kullanılması, açık tür parametrelerini bildirmekten daha açık ve daha özlüdür ve bu nedenle, mümkün olduğunda tercih edilmelidir.

[...]

Joker karakterler ayrıca, alan türleri, yerel değişkenler ve diziler olarak yöntem imzalarının dışında kullanılabilme avantajına sahiptir.


0

İkinci yol biraz daha ayrıntılıdır, ancak Tiçine bakmanıza izin verir :

for (T shape : shapes) {
    ...
}

Anladığım kadarıyla tek fark bu.

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.