'E', 'T' ve '' 'arasındaki fark nedir? Java jenerikleri için?


261

Ben böyle Java kodu rastlamak:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

Yukarıdakilerin üçü arasındaki fark nedir ve Java'da bu tür sınıf veya arayüz bildirimleri olarak adlandırılan şey nedir?


1
Herhangi bir fark olduğundan şüpheliyim. Sanırım bu tür parametre için sadece bir isim. Ve sonuncusu bile geçerli mi?
CodesInChaos

Yanıtlar:


231

İlk ikisi arasında bir fark yok - sadece type parametresi ( Eveya T) için farklı adlar kullanıyorlar .

Üçüncüsü geçerli bir bildirim değil - bir tür argümanı sağlarken kullanılan ?bir joker karakter olarak kullanılır , örneğin, bir tür listeye gönderme yapan anlamına gelir, ancak ne olduğunu bilmiyoruz.List<?> foo = ...foo

Tüm bunlar oldukça büyük bir konu olan jeneriklerdir . Elbette daha fazla mevcut olmasına rağmen, aşağıdaki kaynaklar aracılığıyla bu konuda bilgi edinmek isteyebilirsiniz:


1
PDF bağlantısı kopmuş gibi görünüyor. Burada bir kopya gibi görünen bir şey buldum , ancak orijinalin neye benzediğini bilmediğim için% 100 emin olamıyorum.
John

2
@John: Evet, işte bu. İster bir ister Oracle olsun, bir bağlantıyı düzenleyecek ...
Jon Skeet

T, E ve dışında bir şey var mı? jenerik ilaçlarda kullanılır? Eğer öyleyse bunlar nedir ve ne anlama geliyorlar?
sofs1

1
@ sofs1: Özel bir şey yok Tve Ebunlar sadece tanımlayıcılar. KeyValuePair<K, V>Örneğin yazabilirsiniz . ?özel bir anlamı var.
Jon Skeet

215

Her şeyden daha fazla kongre.

  • T bir Tip anlamına gelir
  • Ebir Öğe olması amaçlanmıştır ( List<E>: bir Öğe listesi)
  • KAnahtar (a Map<K,V>)
  • V Değer (bir dönüş değeri veya eşlenmiş değer olarak)

Tamamen değiştirilebilirler (aynı beyanda yer alan çelişkiler de olsa).


20
<> Arasındaki harf sadece bir isimdir. Cevabınızda tanımladığınız şey sadece sözleşmelerdir. Tek bir büyük harf olması bile gerekmez; sınıflar, değişkenler vb. verebildiğiniz gibi istediğiniz herhangi bir adı kullanabilirsiniz.
Jesper

Daha ayrıntılı ve net bir açıklama bu makalede mevcuttur oracle.com/technetwork/articles/java/…
fgul

6
Soru işaretini açıklamadınız. Downvoted.
shinzou

129

Önceki cevaplar tip parametrelerini (T, E, vb.) Açıklar, ancak joker karakteri, "?" Veya aralarındaki farkları açıklamaz, bu yüzden buna değineceğim.

Birincisi, açık olmak gerekirse: joker karakter ve tür parametreleri aynı değildir. Tür parametrelerinin, bir kapsamın türünü temsil eden bir tür değişkeni (ör. T) tanımladığı durumlarda, joker karakter bunu yapmaz: joker karakter yalnızca genel bir tür için kullanabileceğiniz bir dizi izin verilen tür tanımlar. Herhangi bir sınırlama ( extendsveya super) olmadan , joker karakter "burada herhangi bir tür kullanın" anlamına gelir.

Joker karakter her zaman açılı ayraçlar arasında gelir ve yalnızca genel bir tür bağlamında bir anlamı vardır:

public void foo(List<?> listOfAnyType) {...}  // pass a List of any type

asla

public <?> ? bar(? someType) {...}  // error. Must use type params here

veya

public class MyGeneric ? {      // error
    public ? getFoo() { ... }   // error
    ...
}

Örtüştükleri yerde daha kafa karıştırıcı oluyor. Örneğin:

List<T> fooList;  // A list which will be of type T, when T is chosen.
                  // Requires T was defined above in this scope
List<?> barList;  // A list of some type, decided elsewhere. You can do
                  // this anywhere, no T required.

Yöntem tanımlarıyla mümkün olan şeylerde çok fazla çakışma var. Aşağıdakiler, işlevsel olarak özdeştir:

public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething)  {...}

Öyleyse, çakışma varsa neden birini veya diğerini kullanalım? Bazen, dürüstçe sadece stil: bazı insanlar bir tür parametresine ihtiyacınız yoksa , kodu daha basit / daha okunabilir hale getirmek için bir joker karakter kullanmanız gerektiğini söyler . Yukarıda açıkladığım bir temel fark: tip parametreleri, kapsamın başka bir yerinde kullanabileceğiniz bir tip değişkeni (örn. T) tanımlar; joker karakter kullanmaz. Aksi takdirde, tür parametreleri ve joker karakter arasında iki büyük fark vardır:

Tür parametrelerinin çoklu sınırlayıcı sınıfları olabilir; joker karakter şunları yapamaz:

public class Foo <T extends Comparable<T> & Cloneable> {...}

Joker karakter daha düşük sınırlara sahip olabilir; params yazın:

public void bar(List<? super Integer> list) {...}

Yukarıda, joker karakterde bir alt sınır olarak List<? super Integer>tanımlanır Integer, yani Liste türünün Tamsayı veya süper Tamsayı türü olması gerekir. Genel tip sınırlaması, ayrıntılı olarak ele almak istediğim şeyin ötesinde. Kısacası, genel bir türün hangi türler olabileceğini tanımlamanızı sağlar . Bu jeneriklerin polimorfik olarak tedavi edilmesini mümkün kılar. Örneğin:

public void foo(List<? extends Number> numbers) {...}

Bir geçebilir List<Integer>, List<Float>, List<Byte>, vb numbers. Tip sınırlaması olmadan, bu işe yaramaz - jenerikler böyle.

Son olarak, başka bir şekilde yapabileceğinizi düşünmediğim bir şey yapmak için joker karakteri kullanan bir yöntem tanımı:

public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
    numberSuper.add(elem);
}

numberSuperbir Sayı Listesi veya Sayının herhangi bir süper türü (ör. List<Object>) olabilir ve elemSayı veya herhangi bir alt tür olmalıdır. Tüm sınırlamalarda, derleyici .add()türünün güvenli olduğundan emin olabilir .


"public void foo (Liste <? Sayı> sayıları genişletir) {...}" "genişletme" "süper" olmalı mı?
1a1a11a

1
Hayır. Bu örneğin amacı, bir Sayı Listesini ve Sayı alt türlerini polimorf olarak destekleyen bir imza göstermektir. Bunun için "genişletme" kullanırsınız. Yani, "Bana bir Numara Listesi veya Numarayı genişleten herhangi bir şey ilet" (Liste <Integer>, Liste <Float>, her neyse). Bunun gibi bir yöntem daha sonra liste boyunca yinelenebilir ve her eleman için "e", örneğin e.floatValue () komutunu yürütebilir. Hangi Number alt türünü (uzantısını) ilettiğiniz önemli değildir - .floatValue () bir Number yöntemi olduğundan her zaman ".floatValue ()" işlevini kullanabilirsiniz.
Hawkeye Parker

Son örneğinizde, yöntem daha genel bir şeye izin vermediği için "Liste <? Süper Sayı>" basitçe "Liste <Sayı>" olabilir.
jessarah

@jessarah hayır. Belki benim örneğim belirsiz, ama örnekte adder () bir liste <Object> (nesne bir üst sınıf sınıfı) alabilir söz. Bunu yapabilmesini istiyorsanız, "List <? Super Number>" imzasına sahip olması gerekir. Tam da buradaki "süper" nin anlamı.
Hawkeye Parker

2
Bu cevap, joker karakterler ve tür parametreleri arasındaki farkları açıklamakta çok iyidir, bu cevapla ilgili özel bir soru olmalıdır. Son zamanlarda jeneriklerde daha derinlere gidiyorum ve bu cevap bir şeyleri bir araya getirmeme çok yardımcı oldu, kısaca birçok kesin bilgi, teşekkürler!
Testo Testini

27

Tür değişkeni <T>, belirttiğiniz ilkel olmayan türler olabilir: herhangi bir sınıf türü, herhangi bir arabirim türü, herhangi bir dizi türü veya hatta başka bir tür değişkeni.

En sık kullanılan tür parametre adları:

  • E - Elemanı (Java Collections Framework tarafından yaygın olarak kullanılır)
  • K - Anahtar
  • N - Sayı
  • T - Türü
  • V - Değer

Java 7'de şu şekilde başlamasına izin verilir:

Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6

3

En sık kullanılan tür parametre adları:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

Bu adların Java SE API'sında kullanıldığını göreceksiniz


2

derleyici, aşağıdaki gibi bir işlev oluşturduğunda her joker karakter için (örneğin, Listedeki soru işareti) bir yakalama yapar:

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

Ancak V gibi genel bir tür tamam ve genel bir yöntem haline gelir :

<V>void foo(List<V> list) {
    list.put(list.get())
}
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.