Elmas operatörü (<>), Java 7'de tanıtıldığında, öncelikle yalnızca somut sınıf örnekleme ifadelerini destekliyordu ve anonim iç sınıfları açıkça hariç tutuyordu. Geliştiriciler new Comparable<String>() { ... } gibi yapılar denediklerinde, derleyici elmas varyasyonunu new Comparable<>() { ... } şeklinde reddetti çünkü anonim sınıflar, çıkarılan tip parametrelerine atıfta bulunan tip üyeleri tanımlayabilir ve bu durum sağlıksız tip sistemleri yaratabilirdi.
Temel sorun belirlenemeyen tipler üzerindeydi. Anonim sınıflar, sınıfın tip parametrelerine bağımlı olan yöntemler veya alanlar tanımlayabilir. Derleyici, elmas için karmaşık bir kesişim tipi çıkarırsa, örneğin bir anonim sınıf void foo(Box<T> t) {} tanımlar, T ifadesi kaynak kodda ifade edilemeyen yakalanan joker karakteri temsil edebilirdi. Bu, anonim sınıfın API'sinin adı veya kontrolü kaynak düzeyinde mümkün olmayan tipler içermesine neden oldu ve bu da Java'nın kamu API'lerinde tüm tiplerin belirlenebilir olma gerekliliğini ihlal etti.
Java 9, JEP 213 aracılığıyla belirlenebilir tipler analizi uygulayarak bunu çözdü. Derleyici, anonim sınıf oluşturumu için çıkarılan tipin belirlenebilir olduğunu doğruladı; yani, Java tip sözdizimini kullanarak ifade edilebilen bir tip olmalıdır. Aşağıdaki örnek, yasal bir kullanımı göstermektedir:
// Java 9+ için Geçerli Comparator<String> c = new Comparator<>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } };
Eğer çıkarım, belirlenemeyen joker karakterler ya da kesişimler gibi karmaşık tipler üretirse, derleyici açık tip argümanları gerektirmeye geri döner. Bu, yaygın durumlar için kısa sözdizimi sağlarken tip güvenliğini garanti eder.
Java 8 tabanlı bir finansal ticaret platformunda, geliştirme ekibi binlerce olay işleyicisini sürdürüyordu. Bu işleyiciler, sipariş eşleştirme motoru boyunca anonim Comparator<TradeEvent> ve Predicate<MarketData> uygulamalarını kullanıyordu, bu da önemli görsel gürültü yaratan açık tip argümanları gerektiriyordu.
Ekip, fazlalığı azaltmak için üç yaklaşım düşündü. İlk yaklaşım, tüm anonim sınıfları lambda ifadelerine dönüştürmekti. Bu, basit durumlar için uzunluğu ortadan kaldırdı, ancak birçok işleyici, lambda yeteneklerini aşan özel yardımcı yöntemler veya istisna işleme blokları gerektiriyordu. Bu sınırlama, isimlendirilmiş iç sınıflara garip bir yeniden yapılandırma yapılmasını zorunlu kıldı ve sınıf sayısını artırarak davranışın yerelliğini azalttı.
İkinci yaklaşım açık tip argümanlarını koruma önerdi. Bu, tam işlevselliği korudu ve mevcut Java 8 altyapısıyla çalıştı, ancak bakım yükünü sürdürdü. Geliştiriciler, tip imzalarını değiştirdiklerinde sık sık birleştirme çatışmalarıyla karşılaştılar ve gereksiz deklarasyonlar, hata ayıklama oturumlarında bilişsel yükü artırdı.
Üçüncü yaklaşım, anonim sınıflar için elmas operatörü desteğini kullanmak üzere Java 9'a yükseltme önerdi. Geçiş maliyetini üretkenlik kazançlarıyla değerlendirdikten sonra, ekip Java 9 yükseltmesini seçti çünkü platformun Jigsaw modül sistemi entegrasyonu gerektiriyordu. Belirlenebilir tipler analizi, new Comparator<>() { public int compare(TradeEvent a, TradeEvent b) { ... } } yazmalarını sağlarken derleyici TradeEvent'in belirlenebilir bir tip temsil ettiğini doğruladı.
Bu değişim, ortalama işleyici tanımını dört satırdan bire indirdi ve yaklaşık 2,400 satır gereksiz tip bildirimlerini ortadan kaldırdı. Sonuç olarak, genel modüllerde birleştirme çatışmaları önemli ölçüde azaldı ve açık tip argümanlarını senkronize etme ihtiyacını ortadan kaldırdı. Geliştirme hızı, azaltılmış yeniden yapılandırma yükü sayesinde sonraki çeyreklerde yüzde onbeş oranında arttı.
Elmas operatörü, ham türlerde genel yapıcılar için tip argümanlarını çıkarırken neden başarısız olur?
new ArrayList()<> gibi bir ham sınıf örneği oluşturulurken, elmas operatörü tip argümanlarını çıkaramaz çünkü ham türler genel bilgileri tamamen siler. Derleyici, ham türü hiçbir tip parametresi olmayan bir tür olarak ele alır, bu da çıkarımı imkansız hale getirir çünkü yapıcı imzası kendisi parametreleme kaybeder. Adaylar genellikle bunu denetlenmemiş dönüş uyarılarıyla karıştırır, ancak temel sorun, ham tür bağlamlarında genel meta verilerin tamamen silinmesidir, yalnızca denetlenmemiş işlemlerle değil.
Poli ifadeler ve elmas operatörü arasındaki etkileşim, yöntem aşırı yüklenmesi çözümlemesini nasıl etkiler?
Elmas operatörü, atama bağlamına bağlı bir poli ifade oluşturur. process(new ArrayList<>()) gibi yöntem çağırma bağlamlarında, derleyici, tip çıkarımını tamamlamadan önce hedef tipi yöntemlerin resmi parametrelerinden belirlemek zorundadır. Bu, iki yönlü bir bağımlılık yaratır: yöntemlerin geçerliliği çıkarılan türe bağlıdır, ancak çıkarılan tip hedef tipe bağlıdır. Derleyici, kısıtlama oluşturma ve entegre etme aşamaları aracılığıyla bunu çözer, muhtemelen açık tip argümanları ile olması gerektiğinden farklı aşırı yüklemeleri seçer. Adaylar, aşırı yüklenmenin tam tip çıkarımından önce gerçekleştiğini genellikle göz ardı ederler ve birden fazla aşırı yüklemenin eşleşebileceği durumlarda sürpriz derleme zamanı hatalarıyla karşılaşırlar.
Belirlenebilir tipler kısıtlamasını, dizi oluşturma ile ilgilenen yeniden verilebilir tipler gereksiniminden ne ayırır?
Her iki kısıtlama da belirli genel işlemleri engellese de, belirlenebilir tipler (elmas operatörü çıkarımı ile ilgili) kaynak kodda ifade edilebilen tipleri garanti ederken, yeniden verilebilir tipler (new T[10] ile ilgili) çalışma zamanı tip bilgisi gerektirir. List<String> gibi bir tip belirlenebilir ancak yeniden verilebilir değildir. Adaylar genellikle bu kısıtlamaları karıştırır ve belirlenemeyen tiplerin dizi depolama istisnalarıyla benzer çalışma zamanı güvenlik riskleri taşıdığına inanırlar. Gerçekte, belirlenemeyen tipler, kaynak seviyesinde tip ifade edilebilirliğini ve API tutarlılığını tehlikeye atarken, yeniden verilebilir tipler çalışma zamanı tip güvenliğini tehlikeye atar. Bu ayrımı anlamak, hem anonim sınıflarla hem de dizi tabanlı eski kodla uyumlu kalan genel API'ler tasarlarken kritik öneme sahiptir.