Bu yazıda, pek yakında piyasaya çıkacak olan Java SE 7'nin Java programlama diline getirdiği yeniliklere göz atacağız. Yeni uyarlamanın tartışmalarını J2SE 5.0 ile eklenen yeniliklerin—soysallık🔎 (İng., generics), açımlamalar (İng., annotations), sabit listeleri (İng., enum types), vd.—getirdiği zorlu öğrenme eğrisini hatırlayıp içi ürpererek izleyenlere müjdeyi vererek başlayalım: daha önceleri eklenmesi düşünülen birim (İng. module) ve kod örtüsü (İng. closure) kavramlarının bir sonraki uyarlamaya bırakılması nedeniyle, yeni uyarlamanın J2SE 5.0'de olduğu gibi zorlayıcı olması söz konusu değil. Ama, siz yine de yerlerinizde rahat oturmayın; çünkü, bu sadece bir erteleme, her iki kavram da eninde sonunda Java dilinin üstkavram dağarcığına—yani, problem uzayındaki kavramları koda dökerken kullanılan kavramlar listesine—eklenecek.1
Yeniliklerin içeriğine girmeden önce, okumakta olduğunuz yazının günlükteki diğer yazılardan belirgin bir farklılık gösterdiğini söylemekte yarar var. Daha öncekiler tek bir konuya odaklı ve basit içerikli iken, bu yazı Java altyapınızın sağlam olduğunu varsayıyor ve farklı konulara atıfta bulunuyor. Bir diğer uyarı da yenilikleri deneyip kullanmak isteyeceklere yönelik: söz konusu yeniliklerin hiçbiri yeni bir Bytecode komutu kullanılması sonucunu doğurmadığı için, Java SE 7 uyumlu bir derleyici ile üretilen sınıf dosyaları daha önceki uyarlamaların JSM'leri tarafından çalıştırılabilecektir.2
Kaynak Yönetimli
Java'yı C/C++ dilinden ayıran temel özelliklerden birisi, geliştiricinin insan olduğu ve hata yapabileceği varsayımına binaen bazı sorumlulukları geliştiriciden derleyici ve sanal makine gibi sistem yazılımlarına aktarmasıdır. Bunun en bilinen örneği, çöp toplayıcı yardımıyla yığın bellek yönetimini yazılım geliştirme aşaması etkinliği olmaktan çıkarmasıdır.3 Ancak, çöp toplama desteği kaynak yönetme kaygılarının tamamen ortadan kalktığı anlamına gelmez; geliştiriciler yığın bellek dışındaki dosya, ağ bağlantısı, veri tabanı bağlantısı gibi kaynakları yönetmek zorundadırlar. Örnek olarak, bir veri tabanı tablosunun yazma kilidini düşünün. Kilidin sahibi olan program işini bitirip tabloyu serbest bırakmadıkça bir diğer programın aynı tabloda değişiklik yapması mümkün olmayacaktır; işin bitirilip ne zaman dönüleceğini ise ancak programı yazan geliştirici(ler) bilebilir. Dolayısıyla, bu tür kaynakların mümkün olan en erken noktada geliştirici tarafından döndürülmesi gerekir. Aksi bir durum, aynı kaynağın diğer kullanıcılarının boş yere beklemesi anlamını taşır. İşte bu yüzden, yeni uyarlama ile birlikte Java,
Ne denmek istendiğini aşağıdaki kod şablonundan izleyerek görelim. İlk satırda,
Hata kotarımını ilgilendiren ikinci yenilik, yeniden atılan ayrıksı durum nesnelerinin ele alınışına yönelik. Java SE 7 öncesinde hata verecek aşağıdaki kod parçasından izleyelim.
Bu bölümü küçük bir uyarı ile kapatalım: Değindiğimiz her iki yeniliğin kullanımında da kotarıcı parametresinin
Göz atacağımız son yenilik olan
J2SE 5.0 sürümünde eklenen soysallığı kullanan kod ile J2SE 5.0 öncesindeki kodun bir arada kullanılabilmesi için derleme sırasında tür silme adı verilen bir dönüşüme başvurulur. Bu, soysal türlere dair tür argümanlarının korunmayacağı anlamını taşır. Mesela, örneğimizin 8. satırında argüman olarak geçirilen ve [statik] türleri
Örneğimizdeki uyarının ortadan kaldırılması 8. satır öncesine
Yeniliklerin içeriğine girmeden önce, okumakta olduğunuz yazının günlükteki diğer yazılardan belirgin bir farklılık gösterdiğini söylemekte yarar var. Daha öncekiler tek bir konuya odaklı ve basit içerikli iken, bu yazı Java altyapınızın sağlam olduğunu varsayıyor ve farklı konulara atıfta bulunuyor. Bir diğer uyarı da yenilikleri deneyip kullanmak isteyeceklere yönelik: söz konusu yeniliklerin hiçbiri yeni bir Bytecode komutu kullanılması sonucunu doğurmadığı için, Java SE 7 uyumlu bir derleyici ile üretilen sınıf dosyaları daha önceki uyarlamaların JSM'leri tarafından çalıştırılabilecektir.2
switch-case
Komutlarında String Türlü Seçici İfade- İkili Taban Gösterimi ve Rakamların Gruplanması
- Kaynak Yönetimli
try
Bloğu - Kotarıcı Paylaşımı ve Yeniden Fırlatmanın Yeni Anlamı
- Sınırlı Tür Çıkarsama: Elmas İşleci
SafeVarargs
Açımlaması
switch-case
Komutlarında String Türlü Seçici İfade
switch-case
komutlarındaki seçici ifadenin türü hakkındaki kısıtlamanın String
türünü kapsayacak şekilde gevşetilmesi ile başlayalım. Buna göre, eskiden seçici ifadenin tamsayı türleri ve char
'a kısıtlı olması nedeniyle geçerli olmayan aşağıdaki kod parçası, Java SE 7 ile birlikte Java derleyicileri tarafından kabul görecektir.import java.util.Locale; import static java.lang.System; ... devamMı: do { switch (seçim.toUpperCase(new Locale("tr"))) { case "EVET": case "YES": ...; break devamMı; case "HAYIR": case "NO": ...; break devamMı; default: out.println("Evet veya Hayır giriniz!!!"); } } while(true); ...
İkili Taban Gösterimi ve Rakamların Gruplanması
Bir diğer küçük yenilik, tamsayı sabitlerin artık 8'li, 10'lu ve 16'lı tabanın yanısıra 2'li tabanda da yazılabilecek olması. Ayrıca, sayısal sabitlerin, ister tamsayı türlü olsun isterse kayan noktalı, doğru ve çabuk algılanmasını sağlamak için altçizgi karakteri ('_'
) kullanılarak dilendiği gibi gruplanması da mümkün.double bütçeAçığı = 8_000_000_000_000_000; double piSayısı = 3.1415_92_6535_897_93; short bayrakYazmacı = 0b0000_010010_000100; ...Bir sonraki yeniliğe geçmeden bir uyarıda bulunalım: gruplama karakteri sayıyı oluşturan bileşenlerin (tamsayı kısmı, kesir kısmı, üs kısmı) başında veya sonunda kullanılamaz. Buna göre, aşağıdaki örneklerin hiçbiri Java derleyicisinin kabulünü görmeyecektir.
double bütçeAçığı = _8000_000_000_000_000; double piSayısı = 3_.1415_92_6535_897_93; short bayrakYazmacı = 0b0000_010010_000100_; double büyükSayı = 0x1.2345p_123; double küçükSayı = 0x1.2345p-123_; ...
Kaynak Yönetimli try
Bloğu
Java'yı C/C++ dilinden ayıran temel özelliklerden birisi, geliştiricinin insan olduğu ve hata yapabileceği varsayımına binaen bazı sorumlulukları geliştiriciden derleyici ve sanal makine gibi sistem yazılımlarına aktarmasıdır. Bunun en bilinen örneği, çöp toplayıcı yardımıyla yığın bellek yönetimini yazılım geliştirme aşaması etkinliği olmaktan çıkarmasıdır.3 Ancak, çöp toplama desteği kaynak yönetme kaygılarının tamamen ortadan kalktığı anlamına gelmez; geliştiriciler yığın bellek dışındaki dosya, ağ bağlantısı, veri tabanı bağlantısı gibi kaynakları yönetmek zorundadırlar. Örnek olarak, bir veri tabanı tablosunun yazma kilidini düşünün. Kilidin sahibi olan program işini bitirip tabloyu serbest bırakmadıkça bir diğer programın aynı tabloda değişiklik yapması mümkün olmayacaktır; işin bitirilip ne zaman dönüleceğini ise ancak programı yazan geliştirici(ler) bilebilir. Dolayısıyla, bu tür kaynakların mümkün olan en erken noktada geliştirici tarafından döndürülmesi gerekir. Aksi bir durum, aynı kaynağın diğer kullanıcılarının boş yere beklemesi anlamını taşır. İşte bu yüzden, yeni uyarlama ile birlikte Java, try
bloğu içinde yararlanılan kaynakların otomatik olarak döndürülmesini garanti ederek olası programcı hatasının önüne geçen kaynak yönetimli try
bloğunu sunmaktadır.Ne denmek istendiğini aşağıdaki kod şablonundan izleyerek görelim. İlk satırda,
try
'ı takip eden ayraç çifti arasındaki ilkleme komutu, Java derleyicisine program içinde d
tanımlayıcısı yoluyla temsil edilen disk dosyasının otomatik olarak yönetilmesi istendiğini belirtiyor. Bu istemin yerine getirilmesi, işlenmesi ne şekilde sona ererse ersin, try
bloğu sonunda söz konusu kaynağın geri döndürüleceği anlamına gelir. try (FileInputStream d = new FileInputStream("Veri.dat")) { // d'yi kullan. } catch (IOException e) { ... }Örneğimiz otomatik olarak yönetilmesi istenen kaynağın Java SE 7 ile birlikte eklenen
java.lang.AutoCloseable
arayüzünü gerçekleştirmesiyle geçerlilik kazanır. Bu, java.lang.Closeable
arayüzünün bu arayüzden kalıtlayacak şekilde tanımının değiştirilmesi nedeniyle, G/Ç sınıflarınca temsil edilen tüm kaynakların kaynak yönetimli try
bloğuyla otomatik olarak yönetilebileceği anlamına gelir.finally
kotarıcısını (İng., handler) bilenleriniz—sabırsızlıklarını görür gibiyim—kaynak yönetimli try
komutunun aslında pek de bir şey getirmediğini düşünebilir. Ne de olsa, yukarıda anlatılan işlev kaynak döndürme komutunu finally
bloğu içine yazarak da sağlanabilir. Bir diğer deyişle, aşağıdaki kod şablonu da işimizi görecektir.try { // d'yi kullan. } catch (IOException e ) { ... } finally { d.close(); }Bu itirazı seslendiren arkadaşlar bir yere kadar haklı. Ne var ki, şu iki nokta onları da ikna edecektir.
try
vecatch
bloklarının işlemesi sırasında JSM sonlan(dırıl)acak olursa veya söz konusu işletilen bloğun izleği (İng., thread) durdurulur veya sonlandırılacak olursa,finally
bloğunun işletileceği garantisi verilemez.- Kaynak yönetimli
try
komutunun varlığı, programcıya hatırlatıcı olmasının yanısıra belgeleme yönüyle de katkıda bulunur.
Kotarıcı Paylaşımı ve Yeniden Fırlatmanın Yeni Anlamı
Kimi zaman, birden çok ayrıksı durum kotarıcısının aynı gövdeye sahip olması durumuyla karşılaşabiliriz. Aynı kodun tüm kotarıcılara tekrar tekrar yazılması, kod şişmesinin yanında gerekebilecek bir değişikliğin birden çok yerde yapılması sonucunu doğurur. İşte bu noktada, Java'nın yeni uyarlaması programcıya kotarıcı paylaşımı şansını verir. Buna göre, aşağıda verilentry
bloğu içindeki komutların icra edilmesi sırasında AyrıksıDurumA
veya AyrıksıDurumB
türlü bir ayrıksı durum ortaya çıkacak olursa, aynı kotarıcı soruna çözüm bulmaya çalışacaktır. ... try { // AyrıksıDurumA, AyrıksıDurumB veya AyrıksıDurumC türünde // ayrıksı durumların ortaya çıkabileceği bir şeyler yap. } catch (AyrıksıDurumA | AyrıksıDurumB e} { ... } catch (AyrıksıDurumC e) { ... } finally { ... } ...Kotarıcı paylaşımında şu nokta unutulmamalıdır: ayrıksı durum nesnesi kullanılarak kotarıcı içinde yapılacak şeyler kotarıcı parametresinin türlerinin ortak paydasına sınırlı kalacaktır. Dolayısıyla, yukarıda sağlanan paylaşım örneğinde
e
'nin kullanımı AyrıksıDurumA
ve AyrıksıDurumB
türlerinin ortak atasına gönderilebilecek iletilere sınırlı olacaktır. Bunun doğal bir sonucu olarak, parametrenin türleri arasında biri diğerinin atası olan sınıfların bulunması bir hata olarak görülecek ve söz konusu kod derleyici tarafından kabul edilmeyecektir.4Hata kotarımını ilgilendiren ikinci yenilik, yeniden atılan ayrıksı durum nesnelerinin ele alınışına yönelik. Java SE 7 öncesinde hata verecek aşağıdaki kod parçasından izleyelim.
public void birMetot() throws AyrıksıDurumA, AyrıksıDurumB { ... try { birŞeylerYap(...); // AyrıksıDurumA fırlatabilir. başkaBirŞeylerYap(...); // AyrıksıDurumB fırlatabilir. } catch (Exception e} { ... // Bir şeyler yap. throw e; } ... } // void birMetot() sonuİşlerin yolunda gitmemesi halinde,
birMetot
metodundaki try
bloğu, AyrıksıDurumA
veya AyrıksıDurumB
türlü bir ayrıksı durumla sonlanabilir. Ayrıksı durum nesnesinin fırlatılması sonrasında, Exception
türlü parametreye sahip kotarıcı devreye girecek ve ortalığa çeki düzen verdikten sonra parametreyi yeniden fırlatarak topu metodun çağrıcısına atacaktır. İşte bu noktada, Java SE 7'nin farkı ortaya çıkıyor: derleyicinin try
bloğu içinden çağrılan metotların imzalarını incelemesi sonucunda kotarıcıda yeniden fırlatılan nesnenin Exception
değil de AyrıksıDurumA
veya AyrıksıDurumB
türünde olabileceği bilgisi korunuyor. Bu nedenledir ki, önceki uyarlamalarda Exception
fırlatabileceği ilan edilmek zorunda kalınan birMetot
, artık AyrıksıDurumA
ve AyrıksıDurumB
fırlatabileceğini ilan eden bir imza ile tanımlanabiliyor.Bu bölümü küçük bir uyarı ile kapatalım: Değindiğimiz her iki yeniliğin kullanımında da kotarıcı parametresinin
final
olduğu varsayılır. Bir başka deyişle, söz konusu parametre yeni bir ayrıksı durum nesnesini gösterecek şekilde değiştirilemez; parametrenin değiştirilmesi anlatılan yeniliklerin kullanılmasını engeller.Sınırlı Tür Çıkarsama: Elmas İşleci
Şapkadan çıkaracağımız bir sonraki yenilik de kendini derleyicinin yaptığı ek mesainin programcıya sağladığı kolaylıkla gösteriyor: tür çıkarsama. Ancak, Haskell veya ML bilenleriniz beklentilerini yüksek tutmasın. Çünkü, Java derleyicilerinin yeni uyarlamayla birlikte—soysallığın eklenmesi sonrasında sağlananın üstüne—sağlayacağı tür çıkarsama özelliği soysal türlerin nesnelerinin yaratıldığı kullanımlara kısıtlı. Derleyicinin tür çıkarsama için işaret olarak algıladığı<>
karakterlerinin görünüşünden dolayı elmas işleci olarak da adlandırılan bu yenilik aşağıdaki gibi kullanılabilir.import java.util.*; ... Map<String, Vector<String>> sözlük = new HashMap<String, Vector<String>>(); Map<String, Vector<String>> yeniSözlük = new HashMap<>();
SafeVarargs
Açımlaması
Göz atacağımız son yenilik olan SafeVarargs
açımlaması, sınıf kitaplıkları yazanlarınız dışındakileri doğrudan etkilemiyor. Ancak, kitaplık kullanıcıları da kimi zaman anlamakta zorlandıkları derleyici uyarılarının azalması nedeniyle epey mutlu olacaklar. Ne kastettiğimizin anlaşılması için önce bu yeni açımlamanın ne zaman gerekeceğinin bir örneğini verelim.SVÖrneği.java
import java.util.*; public class SVÖrneği { public static void main(String[] args) { List<Integer> tl1 = Arrays.asList(1, 2, 3); List<Integer> tl2 = Arrays.asList(4, 5); List<List<Integer>> tll = Arrays.asList(tl1, tl2); } // void main(String[]) sonu } // SVÖrneği sınıfının sonu
$ javac -encoding utf-8 -Xlint:unchecked SVÖrneği.java SVÖrneği.java:8: warning: [unchecked] unchecked generic array creation of type java.util.List<java.lang.Integer>[] for varargs parameter List<List<Integer>> tll = Arrays.asList(tl1, tl2); ^ 1 warning
asList
metodunun işini nasıl yaptığını öngörebilen birisi olarak, aynı metodun çağrıldığı 5 ve 6. satırlar onaylanırken 8. satırın uyarı üretmesi, bazılarınıza garip gelebilir. Sanırım soysallıkla ilgili kısa bir açıklama bu arkadaşları ikna edecektir.J2SE 5.0 sürümünde eklenen soysallığı kullanan kod ile J2SE 5.0 öncesindeki kodun bir arada kullanılabilmesi için derleme sırasında tür silme adı verilen bir dönüşüme başvurulur. Bu, soysal türlere dair tür argümanlarının korunmayacağı anlamını taşır. Mesela, örneğimizin 8. satırında argüman olarak geçirilen ve [statik] türleri
List<Integer>
olan tl1
ve tl2
değişkenlerinin türü List
'e dönüştürülecektir. Dolayısıyla, çağırılan metot, argümanları değişik ve birbirinden bağımsız türlerden değerler içerebilecek List
nesneleri olarak görecektir. Bu ise, çağrılan metodun bilerek ya da bilmeyerek Integer
nesneler tutması beklenen liste(ler)i herhangi bir türden nesne tutacak hale döndürmesinin mümkün olduğu anlamına gelir. Buna karşın, derleyicinin böylesine bir kodu reddetmesi de, J2SE 5.0 öncesi ve sonrasında üretilmiş kodların bir arada çalışamayacağı anlamına geleceğinden, kabul edilebilir bir seçenek değildir. Çözüm, olası hataya dikkat çekmek için programcıya verilen bir uyarı ile sağlanır. Örneğimizdeki uyarının ortadan kaldırılması 8. satır öncesine
@SuppressWarnings("unchecked")
açımlamasının konulması ile sağlanabilir. Java SE 7 öncesinde geçerli olan bu çözümün doğurduğu bir sorun, çok sayıda kullanıcısı bulunan metotlarda her kullanıcının söz konusu uyarı ile uğraşmak zorunda kalmasıdır. Bunun yerine, derleyicinin istediği garantinin metot gerçekleştirimcisi tarafından verilmesi daha yerinde olacaktır. İşte, SafeVarargs
açımlamasının sağladığı budur. Örneğimize uyarlayacak olursak, Arrays
sınıfının gerçekleştirimcilerinin kaynak kodu aşağıdaki gibi yazıp derlemesi tüm kullanıcıların yükünü omuzlarından alacaktır.package java.util; ... public class Arrays { ... @SafeVarargs public static <T> List<T> asList(T... değerler) { ... } ... } // java.util.Arrays sınıfının sonu
Arrays.asList
metoduna benzer şekilde SafeVarargs
açımlamasıyla bezenen diğer Java platformu metotları aşağıda verilmiştir.public static <T> boolean java.util.Collections.addAll( Collection<? super T> c, T... elemanlar)
public static <E extends Enum<E>> java.util.EnumSet<E> EnumSet.of(E ilk, E... gerisi)
protected final void javax.swing.SwingWorker.publish(V... parçalar)
SafeVarargs
açımlaması ile bezenmesi düşünülen metotların bazı özellikleri olması gerekir.- Yapıcılara ek olarak, ancak
static
veyafinal
metotlaraSafeVarargs
açımlaması iliştirilebilir. - Yapıcı veya metodun değişken sayıda argüman alıyor olması gerekir.
SafeVarargs
açımlamasıyla ilan edilmesi, derlenmekte olan sınıfın reddedilmesine neden olacaktır, haberiniz ola!- Adı geçen üstkavramlar hakkında bilgi edinmek için, şu videoları izlemek isteyebilirsiniz: Java birimleri, kod örtüleri. Bu videolar, bir sonraki uyarlama ile Java'ya giriş yapacak bu programlama kavramları ile ilgili gerekli bilgileri sağlayacaktır. ↑
- Aslına bakarsanız, Java platformunun JRuby ve Jython gibi dinamik türlemeli dilleri kullanılarak yazılan programların daha hızlı çalışmasını sağlayacak bir yenilik, Java SE 7 ile birlikte gelen JSM'nin yeni bir Bytecode komutunu (
invokedynamic
) desteklemesi durumunu ortaya çıkardı. Dolayısıyla, bu diller kullanılarak yazılacak yeni programların derlenmesi sonucu oluşturulacak sınıf dosyalarının önceki uyarlamaların JSM'leri tarafından çalıştırılmaları mümkün olmayacaktır. Ancak, statik türlemeli olan Java kullanılarak yazılan programlarda böylesine bir durumun söz konusu olmaması nedeniyle bu bizi ilgilendirmiyor. ↑ - Bu noktada, olası bir yanılgıyı düzeltip Sezar'ın hakkını Sezar'a vermemiz gerekli. Her ne kadar Java yaygın kullanımlı diller içinde çöp toplayıcı desteğini getirerek öncü rolünü oynamış olsa da, bu aslında Lisp-temelli dillerde 1960 yılından bu yana bulunan bir özellik. ↑
- Biraz düşünüldüğünde bunun sebebi daha iyi anlaşılacaktır. Böylesine iki sınıfın ortak atası sınıflardan daha genel olanıdır ve kotarıcı parametresine gönderilecek iletiler bu sınıfın desteklediği iletilere sınırlı olacaktır. Yani, kalıtlayan sınıfın gönderilebilecek iletilerin belirlenmesinde—akılları karıştırmak dışında—hiçbir etkisi olmayacaktır. ↑