Java'daki Jenerikler - Generics in Java
Jenerikler , Java programlama diline 2004 yılında J2SE 5.0 sürümü içinde eklenen bir genel programlama tesisidir . Java'nın tür sistemini "derleme zamanı tür güvenliği sağlarken çeşitli türlerdeki nesneler üzerinde çalışacak bir tür veya yönteme" izin verecek şekilde genişletmek için tasarlandılar . En boy derleme zamanı türü güvenliği , 2016'da her durumda garanti edilmediği gösterildiğinden tam olarak sağlanamadı.
Java koleksiyonları çerçeve destekleri jenerik bir koleksiyon örneğinde saklanan nesnelerin türünü belirtmek için.
1998'de Gilad Bracha , Martin Odersky , David Stoutamire ve Philip Wadler , jenerik türleri desteklemek için Java dilinin bir uzantısı olan Generic Java'yı yarattı. Genel Java, joker karakterlerin eklenmesiyle Java'ya dahil edildi.
Hiyerarşi ve sınıflandırma
Göre Java Dil Şartname :
- Bir tür değişkeni , niteliksiz bir tanımlayıcıdır. Tür değişkenleri, genel sınıf bildirimleri, genel arabirim bildirimleri, genel yöntem bildirimleri ve genel oluşturucu bildirimleri tarafından tanıtılır.
- Bir sınıf , bir veya daha fazla tür değişkeni bildirirse geneldir. Bu tür değişkenleri , sınıfın tür parametreleri olarak bilinir . Parametre olarak işlev gören bir veya daha fazla tür değişkeni tanımlar. Genel bir sınıf bildirimi, tür parametresi bölümünün her olası çağrısı için bir tane olmak üzere bir dizi parametreli tür tanımlar. Bu parametreli türlerin tümü, çalışma zamanında aynı sınıfı paylaşır.
- Bir arabirim , bir veya daha fazla tür değişkeni bildiriyorsa geneldir. Bu tür değişkenleri, arabirimin tür parametreleri olarak bilinir. Parametre olarak işlev gören bir veya daha fazla tür değişkeni tanımlar. Genel bir arabirim bildirimi, tür parametresi bölümünün her olası çağrısı için bir tür kümesi tanımlar. Tüm parametreli türler, çalışma zamanında aynı arabirimi paylaşır.
- Bir yöntem , bir veya daha fazla tür değişkeni bildiriyorsa geneldir. Bu tür değişkenleri, yöntemin biçimsel tür parametreleri olarak bilinir. Resmi tür parametre listesinin biçimi, bir sınıf veya arabirimin tür parametre listesiyle aynıdır.
- Bir kurucu , içinde bildirildiği sınıfın kendisinin genel olup olmamasından bağımsız olarak, genel olarak bildirilebilir. Bir yapıcı, bir veya daha fazla tür değişkeni bildirirse geneldir. Bu tür değişkenleri, yapıcının biçimsel tür parametreleri olarak bilinir. Resmi tür parametre listesinin biçimi, genel bir sınıf veya arabirimin tür parametre listesiyle aynıdır.
Motivasyon
Aşağıdaki Java kodu bloğu, jenerikler kullanılmadığında ortaya çıkan bir sorunu göstermektedir. İlk olarak, bir ArrayListtür bildirir Object. Sonra, bir ekler Stringiçin ArrayList. Son olarak, rastgele bir dizgiyi bir tamsayıya çevirmek genellikle mümkün olmadığından, ekleneni geri almaya Stringve bir Integermantık hatasına dönüştürmeye çalışır.
List v = new ArrayList();
v.add("test"); // A String that cannot be cast to an Integer
Integer i = (Integer)v.get(0); // Run time error
Kod hatasız derlenmiş olsa da java.lang.ClassCastException, üçüncü kod satırını yürütürken bir çalışma zamanı istisnası ( ) atar . Bu tür bir mantık hatası, jenerikler kullanılarak derleme sırasında tespit edilebilir ve bunları kullanmak için birincil motivasyondur.
Yukarıdaki kod parçası, jenerikler kullanılarak aşağıdaki gibi yeniden yazılabilir:
List<String> v = new ArrayList<String>();
v.add("test");
Integer i = (Integer)v.get(0); // (type error) compilation-time error
StringKöşeli parantez içindeki tür parametresi , ArrayListoluşturulacağını bildirir String( ArrayList' jenerik Objectbileşenlerinin soyundan gelen ). Jeneriklerde, üçüncü satırı belirli bir türe dönüştürmek artık gerekli değildir, çünkü sonucu , derleyici tarafından oluşturulan kod tarafından v.get(0)tanımlanır String.
Bu fragmanın üçüncü satır mantıksal kusuru olarak algılanabilir derleme derleyici algılar için (daha sonra J2SE 5.0 veya birlikte) hata v.get(0)döner Stringyerine Integer. Daha ayrıntılı bir örnek için bkz.
Arayüzlerin tanımından Listve Iteratorpaketten küçük bir alıntı java.util:
public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
Joker karakterler yazın
Parametreli bir tür için bir tür bağımsız değişkeni, somut bir sınıf veya arabirimle sınırlı değildir. Java, parametreli türler için tür argümanları olarak hizmet etmek üzere tür joker karakterlerinin kullanılmasına izin verir . Joker karakterler, " <?>" biçimindeki tür bağımsız değişkenleridir ; isteğe bağlı olarak bir üst veya alt sınır ile . Bir joker karakter tarafından temsil edilen tam türün bilinmediği göz önüne alındığında, parametreli türleri kullanan bir nesne üzerinde çağrılabilecek yöntemlerin türüne kısıtlamalar getirilir.
a öğesinin öğe türünün Collection<E>bir joker karakterle parametreleştirildiği bir örnek :
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // compile-time error
c.add(null); // allowed
Element türünün ne anlama geldiğini bilmediğimiz ciçin ona nesne ekleyemiyoruz. add()Yöntem türü argüman alır E, ve eleman türü ile Collection<E>genel arabirimi. Gerçek tür argümanı olduğunda ?, bilinmeyen bir tür anlamına gelir. Yönteme ilettiğimiz herhangi bir yöntem argümanı değeri add(), bu bilinmeyen türün bir alt türü olmalıdır. Bunun ne tür olduğunu bilmediğimiz için hiçbir şey aktaramıyoruz. Tek istisna null ; hangi her türden üyesidir.
Bir tür joker karakterinin üst sınırını belirtmek için extendsanahtar sözcük, tür bağımsız değişkeninin sınırlayıcı sınıfın bir alt türü olduğunu belirtmek için kullanılır. Bu List<? extends Number>, verilen listenin, Numbersınıfı genişleten bazı bilinmeyen türdeki nesneleri içerdiği anlamına gelir . Örneğin, liste List<Float>veya olabilir List<Number>. Listeden bir öğe okumak, bir Number. Boş öğelerin eklenmesine de izin verilir.
Yukarıdaki joker karakterlerin kullanılması, tür argümanı olarak somut tür ile parametreli iki tür arasında herhangi bir kalıtım ilişkisi olmadığından esneklik sağlar. Ne List<Number>de List<Integer>diğer bir alt tipi olduğu; Integerbir alt türü olmasına rağmen Number. Bu nedenle, List<Number>parametre olarak alan herhangi bir yöntem, argümanını kabul etmez List<Integer>. Öyle olsaydı, içine a Numberolmayan bir a eklemek mümkün olurdu Integer; hangi tip güvenliğini ihlal eder. Aşağıdakilerin List<Integer>bir alt türü olsaydı , tür güvenliğinin nasıl ihlal edileceğini gösteren bir örnek List<Number>:
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number> according to substitution rule.
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
Joker karakterli çözüm, tür güvenliğini ihlal edecek işlemlere izin vermediği için işe yarar:
List<? extends Number> nums = ints; // OK
nums.add(3.14); // compile-time error
nums.add(null); // allowed
Bir tür joker karakterinin alt sınırlayıcı sınıfını belirtmek için superanahtar sözcük kullanılır. Bu anahtar sözcük, tür bağımsız değişkeninin sınırlayıcı sınıfın bir üst türü olduğunu belirtir. Yani, List<? super Number>temsil edebilir List<Number>veya List<Object>. List<? super Number>Tür öğelerini döndürür olarak tanımlanan bir listeden okuma Object. Böyle bir listeye eklemek için type Number, herhangi bir alt tür Numberveya null (her türün üyesi olan) öğeleri gerekir .
Kitaptan (Yapımcı Uzattı, Tüketici Süper) anımsatıcı PECS Etkili Java tarafından Joshua Bloch (karşılık gelen joker kullanmak hatırlamak için kolay bir yol sağlar Kovaryans ve contravariance Java).
Genel sınıf tanımları
Burada, bir haritadaki bireysel girdileri (değer eşlemelerinin anahtarı) temsil etmek için kullanılabilecek genel bir Java sınıfı örneği verilmiştir :
public class Entry<KeyType, ValueType> {
private final KeyType key;
private final ValueType value;
public Entry(KeyType key, ValueType value) {
this.key = key;
this.value = value;
}
public KeyType getKey() {
return key;
}
public ValueType getValue() {
return value;
}
public String toString() {
return "(" + key + ", " + value + ")";
}
}
Bu genel sınıf, örneğin aşağıdaki şekillerde kullanılabilir:
Entry<String, String> grade = new Entry<String, String>("Mike", "A");
Entry<String, Integer> mark = new Entry<String, Integer>("Mike", 100);
System.out.println("grade: " + grade);
System.out.println("mark: " + mark);
Entry<Integer, Boolean> prime = new Entry<Integer, Boolean>(13, true);
if (prime.getValue()) System.out.println(prime.getKey() + " is prime.");
else System.out.println(prime.getKey() + " is not prime.");
Çıktı:
grade: (Mike, A) mark: (Mike, 100) 13 is prime.
Elmas operatörü
Tür çıkarımı sayesinde , Java SE 7 ve üstü, programcının, yeterince yakın bir bağlamın ima ettiği bir veya daha fazla tür parametresini içeren bir çift açılı ayraç yerine boş bir çift açılı ayraç ( <>, elmas operatörü olarak adlandırılır ) değiştirmesine izin verir . Bu nedenle, yukarıdaki kod örneği şu şekilde yeniden yazılabilir:
Entry
Entry<String, String> grade = new Entry<>("Mike", "A");
Entry<String, Integer> mark = new Entry<>("Mike", 100);
System.out.println("grade: " + grade);
System.out.println("mark: " + mark);
Entry<Integer, Boolean> prime = new Entry<>(13, true);
if (prime.getValue()) System.out.println(prime.getKey() + " is prime.");
else System.out.println(prime.getKey() + " is not prime.");
Genel yöntem tanımları
Yukarıdaki genel sınıfı kullanan bir genel yöntem örneği:
public static <Type> Entry<Type, Type> twice(Type value) {
return new Entry<Type, Type>(value, value);
}
Not: <Type>Yukarıdaki yöntemde birinciyi kaldırırsak, sembolün bildirimini temsil ettiği için derleme hatası alırız ('Tip' sembolü bulamıyoruz).
Çoğu durumda, yöntemin kullanıcısının tür parametrelerini belirtmesine gerek yoktur, çünkü bunlar şu şekilde anlaşılabilir:
Entry<String, String> pair = Entry.twice("Hello");
Gerekirse parametreler açıkça eklenebilir:
Entry<String, String> pair = Entry.<String>twice("Hello");
İlkel türlerin kullanımına izin verilmez ve bunun yerine kutulu sürümler kullanılmalıdır:
Entry<int, int> pair; // Fails compilation. Use Integer instead.
Ayrıca verilen parametrelere dayalı jenerik yöntemler oluşturma imkanı da vardır.
public <Type> Type[] toArray(Type... elements) {
return elements;
}
Bu gibi durumlarda ilkel türleri de kullanamazsınız, örneğin:
Integer[] array = toArray(1, 2, 3, 4, 5, 6);
Fırlatma yan tümcesindeki jenerikler
İstisnaların kendileri genel olamasa da, genel parametreler bir throws yan tümcesinde görünebilir:
public <T extends Throwable> void throwMeConditional(boolean conditional, T exception) throws T {
if (conditional) {
throw exception;
}
}
Tür silme ile ilgili sorunlar
Jenerikler, tür doğruluğu için derleme zamanında kontrol edilir. Genel tür bilgisi daha sonra tür silme adı verilen bir işlemde kaldırılır . Örneğin, normalde rastgele nesneler içeren List<Integer>genel olmayan türe dönüştürülür List. Derleme zamanı denetimi, ortaya çıkan kodun tip-doğru olduğunu garanti eder.
Tip silme nedeniyle, çalışma zamanında tip parametreleri belirlenemez. Örneğin, bir ArrayListçalışma zamanında incelendiğinde, tür silmeden önce bunun bir ArrayList<Integer>veya bir ArrayList<Float>. Birçok kişi bu kısıtlamadan memnun değil. Kısmi yaklaşımlar var. Örneğin, ait oldukları türü belirlemek için tek tek öğeler incelenebilir; örneğin, bir ArrayList öğesinin bir öğesini ArrayListiçeriyorsa Integer, bu ArrayList ile parametrelenmiş olabilir Integer(ancak, veya Integergibi herhangi bir üst öğesiyle parametreleştirilmiş olabilir ).
NumberObject
Bu noktayı gösteren aşağıdaki kod "Eşit" çıktısını verir:
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if (li.getClass() == lf.getClass()) { // evaluates to true
System.out.println("Equal");
}
Tür silmenin bir başka etkisi de, genel bir sınıfın Throwable sınıfını doğrudan veya dolaylı olarak hiçbir şekilde genişletememesidir:
public class GenericException<T> extends Exception
Bunun desteklenmemesinin nedeni, tür silmeden kaynaklanmaktadır:
try {
throw new GenericException<Integer>();
}
catch(GenericException<Integer> e) {
System.err.println("Integer");
}
catch(GenericException<String> e) {
System.err.println("String");
}
Tür silme nedeniyle, çalışma zamanı hangi yakalama bloğunun yürütüleceğini bilemez, bu nedenle bu derleyici tarafından yasaklanmıştır.
Java jenerikleri C++ şablonlarından farklıdır . Java jenerikleri, kullanılan parametreleme türlerinin sayısından bağımsız olarak bir jenerik sınıfın veya işlevin yalnızca bir derlenmiş sürümünü oluşturur. Ayrıca, tür bilgileri derleme zamanında doğrulandığından ve derlenmiş koda dahil edilmediğinden, Java çalışma zamanı ortamının hangi parametreli türün kullanıldığını bilmesi gerekmez. Sonuç olarak, parametreli bir türde bir Java sınıfını başlatmak imkansızdır çünkü örnekleme, tür bilinmiyorsa kullanılamayan bir kurucuya çağrı yapılmasını gerektirir.
Örneğin, aşağıdaki kod derlenemez:
<T> T instantiateElementType(List<T> arg) {
return new T(); //causes a compile error
}
Çalışma zamanında genel sınıf başına yalnızca bir kopya olduğundan, statik değişkenler , tür parametreleri ne olursa olsun sınıfın tüm örnekleri arasında paylaşılır. Sonuç olarak, type parametresi statik değişkenlerin bildiriminde veya statik yöntemlerde kullanılamaz.
Jeneriklerle ilgili proje
Valhalla Projesi , potansiyel olarak Java 10'dan sonraki sürümler için geliştirilmiş Java jeneriklerini ve dil özelliklerini kuluçkaya yatıran deneysel bir projedir. Potansiyel geliştirmeler şunları içerir:
- genel uzmanlaşma , örneğin Liste<int>
- birleştirilmiş jenerikler ; gerçek türleri çalışma zamanında kullanılabilir hale getirmek.
Ayrıca bakınız
- Genel programlama
- Şablon metaprogramlama
- Joker karakter (Java)
- C# ve Java'nın Karşılaştırılması
- Java ve C++ Karşılaştırması
Referanslar
- ^ Java Programlama Dili
- ^ Bir ClassCastException, yayınlar veya boş değerler olmadığında bile atılabilir. "Java ve Scala'nın Tip Sistemleri Sağlam Değil" (PDF) .
- ^ GJ: Genel Java
- ^ Java Dili Belirtimi, Üçüncü Baskı , James Gosling, Bill Joy, Guy Steele, Gilad Bracha – Prentice Hall PTR 2005
- ^ Gilad Bracha (5 Temmuz 2004). "Java Programlama Dilinde Jenerikler" (PDF) . www.oracle.com .
- ^ Gilad Bracha (5 Temmuz 2004). "Java Programlama Dilinde Jenerikler" (PDF) . www.oracle.com . P. 5.
-
^ Bracha, Gilad . "Joker karakterler > Bonus > Jenerikler" . Java™ Eğitimleri . Oracle.
...tek istisna, her türden üye olan null'dur...
- ^ http://docs.Oracle.com/javase/7/docs/technotes/guides/language/type-inference-generic-instance-creation.html
- ^ Gafter, Neal (2006-11-05). "Java için Reified Generics" . 2010-04-20 alındı .
- ^ "Java Dil Belirtimi, Bölüm 8.1.2" . Oracle . Erişim tarihi: 24 Ekim 2015 .
- ^ Goetz, Brian. "Valhalla'ya hoş geldiniz!" . OpenJDK posta arşivi . OpenJDK . Erişim tarihi: 12 Ağustos 2014 .