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?
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?
Yanıtlar:
İlk ikisi arasında bir fark yok - sadece type parametresi ( E
veya 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:
T
ve E
bunlar sadece tanımlayıcılar. KeyValuePair<K, V>
Örneğin yazabilirsiniz . ?
özel bir anlamı var.
Her şeyden daha fazla kongre.
T
bir Tip anlamına gelir E
bir Öğe olması amaçlanmıştır ( List<E>
: bir Öğe listesi) K
Anahtar (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).
Ö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 ( extends
veya 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);
}
numberSuper
bir Sayı Listesi veya Sayının herhangi bir süper türü (ör. List<Object>
) olabilir ve elem
Sayı 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 .
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ı:
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
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
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())
}