|
~BöLÜM-6~ |
|
Altuğ B. Altıntaş ©2002
|
|||||||||||||||||||||||||||||||||||||
PolimorfizmPolimorfizm , nesneye yönelik programlamanın önemli özelliklerinden biridir.Sözlük anlamı olarak "bir çok şekil" anlamına gelmektedir.Polimorfizm ile kalıtım konusu ile iç içedir. Kalıtım konusunu geçen bölüm incelemiştik ; kalıtım konusunda iki taraf bulunmaktadır , türeyen sınıf ve ana sınıf .Türeyen sınıf , türetildiği sınıf'a ait tüm özellikleri alır yani ana sınıf ne yapıyorsa , türeyen sınıf'da bu işlemlerin aynısını yapabilir artı türeyen sınıfın kendisine ait bir çok yeni özelliği olabilir.Türeyen sınıf'a ait objenin ,ana sınıf tipindeki değişkene bağlamanın yukarı doğru çevirim(upcasting) kavramı olduğunu geçen bölüm incelemiştik.Burada anlattıklarımızı örnek üzerinde açıklarsak ; ör-PolimorfizmOrnekBir.java
Yukarıdaki örneğimiz üç kavram mevcuttur ,bunlardan biri yukarı doğru çevirim(upcasting) diğeri polimorfizm ve son olarakta geç bağlama (late binding) kavramıdır, ilk iki kavramı , yukarı doğru çevirim ve polimorfizm kavramını açıklayalım.Bu örneğimizde ana sınıf Asker sınıfıdır. Bu sınıfdan türeyen sınıflar ise Er ve Yuzbasi sınıflarıdır. Bu ilişki "bir" ilişkisidir ;
Yani Asker sınıfının yaptığı her işi Er sınıfı veya Yuzbasi sınıfı da yapabilir artı türetilen bu iki sınıf kendisine has özellikler taşıyabilir, bu olayın kalıtım(inheritance) kavramına girdiğini geçen bölümde incelemiştik( bunu tekrar tekrar söylüyorum çünkü çok önemli ) . Asker sınıfı ile Er ve Yuzbasi sınıflarının arasında kalıtımsal bir ilişki bulunmasından dolayı , Asker tipinde parametre kabul eden hazirOl() metoduna Er ve Yuzbasi tipindeki değişkenleri paslayabildik, bu özelliğinde yukarı doğru çevirim (upcasting) olduğunu geçen bölüm incelemiştik. Polimorfizm özelliği ise hazirOl() metodunun içersinde gizlidir.Bu metodun içersinde Asker tipinde olan a değişkeni kendisine gelen 2 değişik objeye bağlanabildi, bunlardan biri Er diğeri ise Yuzbasi dır.Peki bu metodun içersinde neler olmaktadır. Sırası ile açıklarsak , ilk önce Asker objesine bağlı Asker tipindeki değişkeni , hazirOl() metoduna parametre olarak gönderiyoruz , burada herhangi bir terslik yoktur çünkü hazirOl() metodu zaten Asker tipinde parametre kabul etmektedir. Burada dikkat edilmesi gereken husus , hazirOl() metodunun içersinde Asker tipindeki yerel a değişkenimizin , kendi tipinden başka objelere de (Er ve Yuzbasi) bağlanabilmesidir.Yani Asker tipindeki a değişkeni bir çok şekile girmiş olmaktadır , örneğin aşağıdaki ifadelerin hepsi doğrudur ;
Yukarıdaki ifadelere , Asker tipindeki a değişkenin açısından bakarsak , bu değişkenin bir çok objeye bağlanabildiğini görürüz , bu özellik polimorfizm 'dir - tabii ki bu özelliğin temelinde kalıtım(inheritance) yatar . Şimdi sıra geç bağlama (late binding) özelliğinin açıklanmasında.... Geç bağlama (late binding)Öncelikle şunu söylemek isterim , polimorfizm olmadan , geç bağlama özelliğinden bahsedilemez bile , polmorfizm ve geç bağlama (late binding) bir elmanın iki yarısı gibidir. Şimdi kaldığımız yerden devam ediyoruz , Er objesine bağlı Er tipindeki değişkenimizi ( e ) hazirOl() metoduna parametre olarak gönderiyoruz. gösterim-1
Bu size ilk başta hata olarak gelebilir , ama arada kalıtım ilişkisinde dolayı (Er bir Askerdir) nesneye yönelik programlama çercevesinde bu olay doğrudur. En önemli kısım geliyor...., şimdi , hangi objenin selamVer() metodu çağrılacaktır ? Asker objesinin mi ? yoksa Er objesinin mi ? Cevap: Er objesinin , sebebi ise Asker tipindeki değişkenin Er objesine bağlanmış olmasından kaynaklanır ,eğer Er objesinin selamVer() metodu olmasaydı o zaman Asker objesine ait olan selamVer() metodu çağrılacaktı fakat Er sınıfının içersinde , ana sınıfa ait olan (Asker sınıfı) selamVer() metodu iptal edilmiştir(override) bu sebebten dolayı Java , Er objesinin selamVer() metodunu çağrıcaktır . Peki hangi objenin selamVer() metodunun çağrılacağı ne zaman belli olur ? derleme anında mı (compile-time)? yoksa çalışma anında mı(run-time) ? Cevap : çalışma anındadır (run-time) ; sebebi ise , derleme anında hazirOl() metoduna hangi tür obje gönderileceği belli olmamasıdır. Son olarak , Yuzbasi
objesine bağlı Yuzbasi tipindeki değişkenimizi hazirOl()
metoduna parametre olarak gönderiyoruz.Artık bu bize şaşırtıcı
gelmiyor .. devam ediyoruz . Peki şimdi hangi objeye ait selamVer()
metodu çağrılır ?Asker objesinin mi ? yoksa Yuzbasi
objesinin mi ? cevap tabii ki Yuzbasi objesine ait olan selamVer()
metodunun çağrılacağıdır çünkü Asker tipindeki değişkenimiz
heap alanındaki Yuzbasi objesine bağlıdır ve selamVer()
metodu Yuzbasi sınıfının içersinde iptal edilmiştir
(override) .Eğer selamVer() metodu Yuzbasi sınıfının
içersinde iptal edilmeseydi o zaman Asker sınıfına ait (ana sınıf)
selamVer() metodu çağrılacaktı. Aynı şekilde Java hangi
objenin selamVer() metodunun çağrılacağına çalışma-anında
(run-time) da karar verecektir yani geç bağlama özelliği
devreye girmiş olacaktır. Eğer bir metodun hangi objeye ait olduğu
çalışma anında belli oluyorsa bu olaya geç bağlama (late-binding)
denir , bu olayın tam tersi ise erken bağlamadır (early
binding) yani hangi objenin hangi metodunun çağrılacağı derleme anında
bilinmesi.Bu örneğimiz çok fazla basit olduğu için , "niye
! derleme anında hangi obje tipindeki değişkenin
hazirOl() metoduna paslandığını bilemiyelim ki , önce Asker
objesi sonra Er objesi ve en son olarakta Yuzbasi objesi gönderiliyor işte..."
diyebilirsiniz ama aşağıdaki örneğimiz için aynı şeyi söylemeniz
bu kadar kolay olmayacaktır. ör-PolimorfizmOrnekIki
Yukardaki örneğimizdeki kalıtım (inheritance) ilişkisini , UML diyagramında göstermeye çalışırsak ;
Şekil-1 PolimorfizmOrnekIki.java örneğimizde rasgeleSec() metodu , rasgele Hayvan Objeleri oluşturup geri döndürmektedir.Geri döndürülen bu Hayvan objeleri , Hayvan dizisinin içersine atılmaktadır.Hayvan dizisine atılan Kartal ve Timsah objelerine Javanın kızmamasındaki sebep kalıtımdır. Kartal bir Hayvan'dır diyebiliyoruz aynı şekilde Timsah bir Hayvandır diyebiliyoruz , olaylara bu açıdan bakarsak Hayvan dizisinin içersine eleman atarken yukarı doğru çevirim (upcasting) olduğunu farkederiz. Geç bağlama özelliği ise , Hayvan
dizisinin içersindeki elemanlara ait avYakala() metodunu çağırırken
karşımıza çıkar.Buradaki ilginç nokta hangi objenin avYakala()
metodunun çağrılacağının derleme anında(compile-time) bilinemiyor
olmasıdır.Nasıl yani gösterim-2
Uygulamanın çıktısı aşağıdaki gibidir ;
Aynı uygulamamızı tekrardan çalıştırıyorum ;
Tekrar çalıştırıyorum ;
Görüldüğü üzere dizi içersindeki elemanlar her sefersinde farklı olabilmektedir, dizi içersindeki elemanlar ancak çalışma anında (runtime) belli oluyorlar. h[j].avYakala() derken , derleme anında (compile-time) hangi objenin avYakala() metodunun çağrılacağını Java tarafından bilinemez , bu olay ancak çalışma anında (run-time) bilinebilir. Geç bağlama özelliği bu noktada karşımıza çıkar. Geç bağlama (late-binding) nın diğer isimleri , dinamik bağlama (dynamic-binding) veya çalışma anında bağlamadır (runtime-binding).Yukarıdaki örneğimiz ve bu isimler sanırım birbiriyle tam olarak örtüşmektedir. Final ve Geç bağlamaBölüm 5 de final özelliğinin kullanılmasının iki sebebi olabileceğini belirtmiştik (bkz), bir tanesi tasarım değeri ise verimlilik. Verimlilik konusu geç bağlama (late binding) özelliği ile aydınlamış bulunmaktadır , şöyleki , eğer biz bir sınıfı final yaparsak , bu sınıf'a ait tüm metodları final yapmış oluruz veya eğer istersek tek başına bir metodu da final yapabiliriz.Bir metodu final yaparak şunu demiş oluruz , bu metod , türetilmiş olan sınıfların içersindeki diğer metodlar tarafından iptal edilemesin (override).Eğer bir metod iptal edilemezse o zaman geç bağlama (late binding) özelliğide ortadan kalkar. Uygulama içersinde herhangi bir objeye ait normal bir metod (final olmayan) çağrıldığında , Java , acaba doğru objenin uygun metod mu çağrılıyor diye bir kontrol yapar , daha doğrusu geç bağlamaya(late-binding) ihtiyaç var mı kontrolü yapılır. Örneğin Kedi sınıfını göz önüne alalım. Kedi sınıfı final olmadığından dolayı bu sınıfdan türetme yapabiliriz. ör-KediKaplan.java
Kaplan sınıfına ait statik bir metod olan goster() 'in içersinde Kedi tipindeki değişkene bağlı objenin yakalaAv() metodu çağrılmaktadır ama hangi objenin yakalaAv() metodu , Kedi objesine ait olan mı ? yoksa Kaplan objesine ait olan mı ? Java , yakalaAv() metodunu çağırmadan evvel geç bağlama (late-binding) özelliğini devereye sokarak , doğru objeye ait uygun metodu çağırmaya çalışır tabii bu işlemler sırasından verimlilik düşer.Eğer biz Kedi sınıfını final yaparsak veya sadece yakalaAv() metodunu final yaparsak geç bağlama özelliğini kapatmış oluruz böylece verimlilik artar. ör-KediKaplan2.java
KediKaplan2.java örneğimizde,yakalaAv() metodunu final yaparak Kaplan sınıfı içersinde iptal edilmesini engellendik yani geç bağlama(late binding) özelliği kapatmış olduk. Neden Polimorfizm ?Neden polimofizm sorusuna yanıt aramadan evvel , polimorfizm özelliği olmasaydı olayların nasıl gelişeceğini görelim.Nesneye yönelik olmayan programlama dillerini kullanan kişiler için aşağıdaki örneğimiz gayet normal gelebilir. mesaiBasla() metodunun içersindeki if-else ifadelerine dikkat lütfen. ör-IsYeriNon.java
Yukarıdaki örneğimizde nesneye yönelik
programlamaya yakışmayan davranıştır (OOP) , mesaiBasla()
metodunun içersinde yapılmaktadır
IsYeriNon.java örneğimizi nesneye yönelik programlama çercevesinde tekrardan yazarsak ; ör-IsYeri.java
Görüldüğü üzere mesaiBasla()
metodu artık tek satır
Şekil-2 Genişletilebilirlik (Extensibility)Polimorfizm özelliği sayesinde
genişletebilirlik olayı çok basite indirgenmiş bulunmaktadır.Genişletebilirlik
olayı nedir diyenler için
Şekil-3 Yukarıdaki UML diyagramında görüldüğü üzere mevcut hiyerarşiyi genişlettik , toplam 4 adet yeni sınıfı sistemimize eklenmiş bulunmaktayız (GenelMudur , AnalizProgramci , SistemProgramci , Sekreter).Yukarıdaki UML şemasını Java diline çevirelim ;
Yukarıdaki örneğimizde dikkat edilirse mesaiBasla() metodu hala tek satır. Uygulamamızın çıktısı aşağıdaki gibidir ;
Burada yaptığımız iş sadece , Calisan sınıfından yeni sınıflar türetmektir , bu yeni türerilmiş sınıfların (GenelMudur , AnalizProgramci , SistemProgramci , Sekreter) calis() metodlarını çağırmak için ekstra bir yük üslenmedim ( mesaiBaslat() metodunun içersine dikkat edersek ) . Polimorfizm kavramı ve tabii ki geç bağlama özellikleri sayesinde Java bu işleri otomatik olarak bizim yerimize yapmaktadır. Soyut sınıflar ve metodlar (Abstract classes and Methods )Soyut kavramını anlatmadan evvel , IsYeri.java ve BuyukIsyeri.java örneklerini inceliyelim , bu java dosyalarının içersinde hiç bir iş yapmayan ve sanki boşuna boşuna oraya konulmuş gibi zannedebileceğimiz bir sınıf gözümüze çarpar.Bu sınıfın ismi Calisan dır. Calisan sınıfını daha yakından bakarsak ; gösterim-3
Yukarıda görüldüğü üzere Calisan sınıfı hiç bir iş yapmamaktadır. "Diğer sınıflar sanki çok şey mi yapıyor" diyebilirsiniz , cevap : diğer sınıflar en azından ekrana bişeyler basıyor diyebilirim, sonuçta ekrana biseyler bastırmak belki basit bir işlem gibi gözükebilir ama nihayetinde ufakta olsa iş iştir.Neyse sonuç olarak Calisan sınıfı hiçbir iş yapmadığına kanaat getirdik. Akıllara şöyle bir soru daha gelebilir , "Madem ki Calisan sınıfı hiç bir iş yapmıyor , ne diye onu oraya yazdık" , cevap : birleştirici bir rol oynadığı için Calisan sınıfını oraya yazdık diyebilirim.Olayları biraz daha detaylı açıklamaya başlayalım. Soyut sınıflar , şu ana kadar bildiğimiz sınıflardan farklıdırlar . Soyut (abstract) sınıflarımızı direk new() ile objelerini oluşturamayız.Soyut sınıfların var olmasındaki en büyük sebeplerden biri birleştirici bir rol oynamalarından kaynaklanmaktadır. Soyut bir sınıftan türetilmiş alt sınıflar, çok rahat bir şekilde yine bu soyut sınıfa çevrilebilir (yukarı doğru çevirim ) böylece polimorfizm kavramı ve geç bağlama özelliklerinin kullanılması mümkün olur. Bir sınıfın soyut olması için , bu sınıfın içersinde en az bir adet soyut metodunun bulunması gerekir. Soyut metodların gövdesi bulunmaz , yani içi boş hiçbir iş yapmayan metod görünümündedir. Soyut bir sınıfdan türetilmiş alt sınıflar , bu soyut sınıfın içersindeki soyut metodları kesin olarak iptal etmeleri(override) gerekmektedir . Eğer türetilmiş sınıflar soyut olan ana sınıflarına ait bu soyut metodları iptal etmezler ise derleme anında(compile-time) Java tarafından uyarılırlar. gösterim-4
Soyut sınıfların içersinde soyut metodlar olacağı gibi , gövdeleri olan ,yani iş yapan metodlarda bulunabilir.Buraya kadar anlattıklarımızı bir uygulama üzerinde gösterelim ; AbIsYeri.java
Yukarıdaki örneğimizi incelersek ,soyut (abstract) olan Calisan sınıfının 2 adet metodu olduğu görürüz. Bu iki metod dan bir tanesi olan soyut (abstract) calis() metodudur. Calisan sınıfından türeyen sınıflar tarafından bu metod kesin kes iptal edilmek (override) zorundadır.Baştan açıklarsak , eğer bir sınıfın içersinde soyut (abstract) bir metod varsa o zaman bu sınıf da soyut (abstract) olmak zorundadır.Fakat soyut olan sınıfların içersinde normal metodlarda bulunabilir aynı zamIste() metodunun Calisan sınıfının içersinde bulunduğu gibi. Calisan sınıfından türeyen diğer sınıfları incelersek , bu sınıfların hepsinin , calis() metodunu iptal ettiklerini(override) görürüz ama aynı zamanda zamIste() metodu sadece Programci sınıfının içersinde iptal edilmiştir(override).Eğer ana sınıfın içersindeki bir metodun türemiş sınıflar içersinde iptal edilmelerini (override) şansa bırakmak istemiyorsak o zaman bu metodu soyut metod olarak tanımlamamız gerekir.Doğal olarak soyut bir metoda sahip bir sınıf da soyut olur. Dikkat edilmesi gereken ikinci nokta , soyut sınıfları direk olarak new() ile oluşturamıyor olmamızdır.Soyut sınıf demek birleştirici rol oynayan sınıf demektir.AbIsYeri.java uygulamamızın sınıflara ait UML diyagramı aşağıdaki gibidir ;
Şekil-4 UML diyagramından daha net bir biçimde
görmekteyiz ki türemiş sınıflar , ana sınıfa ait calis()
metodunu iptal etmek(override) zorunda bırakılmışlardır Niye soyut sınıf ve metodlara ihtiyaç duyarız ?Örneğin hem cep telefonunun
ekranına hemde monitörün ekranına çizgi çizdirmek istiyoruz fakat
cep telefonu ekranının özellikleri ile monitör ekranının özelliklerinin
birbirinden tamamen farklı olması karşımızda büyük bir
problemdir,.Bu iki ekrana çizgi çizdirmek için değişik sınıflara
ihtiyaç duyulacağı kesindir.Peki nasıl bir yazılım tasarımı yapılmalıdır
ör-CizimProgrami.java
Cizim sınıfımızın içersinde bulunan cizgiCiz() metodu soyut(abstract) değildir fakat noktaCiz() metodu soyuttur ,neden ? Sebebi, cizgiCiz() metodunun ekranlara çizgi çizmek icin noktaCiz() metoduna ihtiyac duymasında kaynaklanır. cizgiCiz() metodunun ihtiyac duydugu tek şey , ekran üzerinde tek bir noktanın nasıl çizileceğini bilmektir , bu bilgiler cizgiCiz() metoduna verildiği sürece sorun yaşanmıyacaktır.Ekrana tek bir noktanın nasıl çizileceğini , Cizim sınıfından türemiş alt sınıflar tarafından verilmektedir. Cizim sınıfından türemiş sınıflara dikkat edilirse (CepTelefonuCizim ve MonitorCizim),bu sınıfların içersinde ana sınıfa ait olan noktaCiz() metodunun iptal edilmiş (override) olduğunu görürüz.Bunun sebebi her bir ekrana ait nokta çiziminin farklı olmasından kaynaklanır.Yukarıdaki uygulamamıza ait sınıflar için UML diyagramı aşağıdaki gibidir.
Şekil-5 Yukarıdaki örneğimizden çıkartılacak olan anafikir şöyledir; eğer bir işlem değişik verilere ihtiyaç duyup aynı işi yapıyorsa , bu işlem soyut(abstract) sınıfların içersinde tanımlanmalıdır. Yapılandırıcılar içersindeki ilginç durumlarYapılandıcılar (constructor) içersinde ne gibi ilginç durumlar olabilir ki diyebilirsiniz? Biraz sonra göstereceğimiz örnek içersinde polimorfizm ve geç bağlama özelliklerinin devreye girmesiyle olaylar biraz karışacaktır.Öncelikle yapılandırıcıların ne işe yaradıklarını bir tekrar edelim. Bir obje kullanıma geçmeden evvel bazı işlemler yapması gerekebilir, örneğin global olan obje değişkenlerine ilk değerlerinin verilmesi gerekebilir veya JDBC(ilerleyen bölümlerde detaylı olarak inceliyeceğiz) bağlantısı ile veri tabanı bağlanıp bazı işlemleri yerine getirmesi gerekebilir , örnekleri çoğaltmak mümkündür....Tüm bu işlemlerin yapılması için gereken yer yapılandırıcıların içerisidir. Buraya kadar sorun yoksa örneğimizi incelemeye başlayabiliriz. ör-Spor.java
Soyut sınıflara ait yapılandırıcılar olabilir.Bölüm-5 de incelediğimiz üzere, sınıflara ait objeleri oluştururken , işin içersinde bir de kalıtım(inheritance) özelliği girdiğinde olayların nasıl değiştiğini incelemiştik.Bir sınıf'a ait obje oluşturulacaksa ,önce bu sınıfın ana sınıfı var mı diye kontrol edilir , yani bu sınıf türetilmiş bir sınıf mı kontrolu yapılır.Eğer bu sınıfın türetildiği ana bir sınıf var ise önce bu ana sınıf'a ait obje oluşturulur daha sonra sıra türeyen sınıfımıza ait objenin oluşturulmasına gelir.Yukarıdaki örneğimizde kalıtım kavramı kullanılmıştır. Ana sınıf Sporcu sınıfıdır ,bu sınıftan türetilmiş olan ise Futbolcu sınıfıdır - Futbolcu bir Sporcudur . Biz Futbolcu sınıfına ait bir obje oluşturmak istersek , bu olayın daha öncesinde Sporcu objesinin oluşacağını hatırladıktan sonra kaldığımız yerden devam edebiliriz. Bu örneğimizdeki aklı karıştırıcı nokta , soyut bir sınıfa ait yapılandırıcı içersinden soyut bir metodun çağrılıyor olmasıdır.Sporcu sınıfının yapılandırıcısına dikkat ederseniz, bu yapılandırıcı içersinden soyut bir metod olan calis() metodu çağrılmıştır. calis() metodu hangi amaçla soyut yapılmış olabilir ? Bu metodun soyut yapılmasındaki tek amaç , alt sınıfların bu metodu iptal etmelerini kesinleştirmek olabilir.Bu örneğimizdeki yanlış , soyut bir sınıfa ait yapılandırıcının içersinden soyut bir metodun çağrılmasıdır. Peki böyle bir yanlış yapıldığında nasıl sonuçlar oluşur.Uygulamamızın çıktısı aşağıdaki gibidir.
Uygulama çıktısındaki 2. satır çok ilginçtir.Oluşan olayları adım adım açıklarsak;
Kalıtım ve Yukarı doğru çevirim (Upcasting)Kalıtım kavramı ve yukarı doğru çevirim (upcasting) , birazdan anlatacağımız aşağıya doğru çevirim (downcasting) konusunun iyi anlaşılması açısından baştan bir daha tekrar edeceğiz. Yukarı doğru çevirim (upcasting) her zaman güvenlidir , sonuçta daha özellikli bir tipden daha genel bir tipe doğru çevirim gerçekleşmiştir.Örneğin elimizde iki tip televizyon bulunsun , biri Xmodel televizyon diğeri Xmodel'den türetilmiş ve daha yeni özelliklere sahip olan Ymodel televizyon.Bu ilişkiyi UML diyagramında gösterirsek.
Şekil-6 UML diyagramımızı Java uygulamasına dönüştürsek ; ör-Televizyon.java
Yukarı doğru çevirim (upcasting) olayında iki taraf vardır , bir tanesi heap alanında objenin kendisi diğer tarafta stack alanında bulunan referans(değişken) . Olaylara televizyon ve kumanda boyutunda bakarsak işin sırrı çözülmeye başlar.Elimizde Xmodel televizyon kumandası olduğunu düşünün ama kumanda Ymodel bir televizyonu gösterirsin(gösterebilir çünkü arada kalıtım ilişkisi vardır - Ymodel televizyon bir Xmodel televizyondur) , o zaman karşımızda duran Ymodel televizyonun teleText() özelliği olmasına rağmen bunu kullanamayız çünkü Xmodel bir televizyon kumandasının , Xmodel televizyon için tasarlandığından üzerinde teleText() düğmesi olmayacaktır.Anlattıklarımızı şekil üzerinde gösterirsek .
Aşağıya doğru çevirim (Downcasting)Aşağıya doğru çevirim (downcasting) , yukarı doğru çevirim'in (upcasting) tam tersidir.Aşağıya doğru çevirim (downcasting) , daha genel bir tipden, daha özellikli bir tipe doğru geçiş demektir ve tehlikelidir.Tehlikelidir çünkü çevrilmeye çalışılan daha özellikli tipe doğru çevirim esnasında sorun çıkma riski yüksektir.Java programlama dilinde aşağıya doğru çevirim (downcasting) yaparken , hangi tipe doğru çevirim yaptığınızı belirtmeniz gerekir, fakat hatırlarsanız yukarı doğru çevirim (upcasting) işleminde böyle bir belirteç koymak zorunda değildik , çünkü oradaki olay daha özellikli bir tipden daha genel bir tipe doğru çevirimdi yani güvenliydi. Anlattıklarımızı örnek üzerinde gösterirsek; Televizyon2.java
Yukarıdaki uygulamamızın çıktısı aşağıdaki gibidir ;
Bu örneğimizde , Xmodel ve Ymodel objelerimiz, Object tipindeki dizinin içersine atılmaktadır. Her sınıf Object sınıfından türetildiği göre , Object dizisinin içersine Xmodel ve Ymodel objelerini rahatlıkla atabiliriz buradaki olay yukarı doğru çevirimdir (upcasting) . Artık elimizde Object dizisi var , bunun içersindeki elemanları tekrardan eski hallerine sokmak için aşağıya doğru çevirim (downcasting) özelliğini kullanmak gereklidir. Daha evvelden de ifade edildiği gibi aşağıya doğru çevirim(downcasting) yapılacaksa , bunun hangi tipe doğru yapılacağı açık olarak belirtilmelidir. for ifadesinin içersine dikkat edilirse, Object dizisinin içersinde elemaları Xmodel objesine dönüştürmek için aşağıdaki ifade kullanılmıştır. gösterim-5
Yukarıdaki ifade sayesinde Object
dizisi içersindeki Object tipinde olan objelerimizi Xmodel
objesine çevirmiş bulunmaktayız.Aslında bu örneğimizde zarardayız
, niye derseniz Yukarı doğru çevirim (upcasting) yaparken , asıl objelerimiz değerlerinden bişey kaybetmezler.Örneğin bu uygulamamızda bir Object dizimizin içersine Xmodel ve Ymodel objelerini atabildik (bunun sebebinin kalıtımdır) . Xmodel ve Ymodel objelerini , Object sınıfına çevirerek , bu objelerimizin asıl tiplerini değiştirmeyiz sadece Object dizisinin içersine atma izni elde ederiz.Yani Object dizimizin içersinde hala Xmodel ve Ymodel objeler bulunmaktadır. Java çalışma anında (run-time) objelerin tiplerini kontrol eder .Eğer bu işlemlerde bir uyumsuzluk varsa bunu hemen kulanıcıya ClassCastException olarak geri döner.Obje tiplerinin çalışma anından tanımlanması (RTTI : Run Time Type Identification) , kodu yazan kişi açısından büyük faydalar içerir.Örneğin yukardaki örneğimizde çalışma anında objelerimizin tiplerini kontrol edip , ilgili objeye ait metodları çağrılabilir. Televizyon3.java
Object dizimizin içersindeki
elemanların hepsinin Object tipinde olma zorunluluğu olduğunu bölüm-3
de incelemiştik.Xmodel ve Ymodel televizyonları yukarı
doğru çevirim özelliği sayesinde Object tipine çevirerek ,
Object dizisi içersine atabildik. Peki Xmodel ve Ymodel
objelerimizin özelliklerini sonsuza kadar geri alamıyacakmıyız Aşağıya doğru çevirim'in (downcasting) tehlikeli olduğunu biliyoruz.Eğer yanlış bir tipe doğru çevirim yaparsak, çalışma anında Java tarafından ClassCastException istisnası (ilerleyen bölümlerde inceliyeceğiz ) ile durduruluruz. Çalışma anında (run-time) yanlış bir tipe çevirimden korkuyorsak instanceof anahtar kelimesini kulanmamız gerekir. Yukarıdaki örneğimizde(Televizyon3.java) instanceof anahtar kelimesi sayesinde çalışma anında , Object dizisi içersindeki elemanların asıl tiplerini kontrol etme imkanına sahip sahip oluruz.Böylece hata oluşma riskini minimum'a indirgeriz. Çalışma anında (run-time) obje tiplerinin tanımlanması (RTTI = Run time type identification) konusunu ilerleyen bölümlerde detaylı bir şekilde inceliyeceğiz.Uygulamamızın çıktısı aşağıdaki gibidir;
Bölüm Sonu //... Sorular
Bu dökümanın her hakkı saklıdır. © Copyright 2002 |
|||||||||||||||||||||||||||||||||||||||