~BöLÜM-6~

Altuğ B. Altıntaş

©2002

 

Polimorfizm

Polimorfizm , 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

class Asker {
      public void selamVer() {
	       System.out.println("Asker Selam verdi");
      }
}  

class Er extends Asker {
      public void selamVer() {
	       System.out.println("Er Selam verdi");
      }
}

class Yuzbasi extends Asker {
       public void selamVer() {
	        System.out.println("Yuzbasi Selam verdi");
       }
}

public class  PolimorfizmOrnekBir {
    
    public static void hazirOl(Asker a) {
	      a.selamVer(); // ! Dikkat !
    }

    public static void main(String args[]) {
	     Asker a = new Asker();
	     Er e = new Er();
	     Yuzbasi y = new Yuzbasi();
	     hazirOl(a); // yukari dogru cevirim ! yok !
	     hazirOl(e); // yukari dogru cevirim (upcasting) 
    	 hazirOl(y); // yukari dogru cevirim (upcasting)
    }
}

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 ;

  • Er bir Askerdir , veya 

  • Yuzbasi bir Askedir , diyebiliriz . 

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 ; 

  • Asker a = new Asker() ;

  • Asker a = new Er();

  • Asker a = new Yuzbasi();

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

hazirOl(e); // yukari dogru cevirim (upcasting)

 

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 

class Hayvan {
    public void avYakala() {
	System.out.println("Hayvan avYakala");
    }
}

class Kartal extends Hayvan {
   public void avYakala() {
	 System.out.println("Kartal avYakala");
   }
}

class Timsah extends Hayvan{
   public void avYakala() {
	 System.out.println("Timsah avYakala");
   }
}

public class PolimorfizmOrnekIki {
    
    public static Hayvan rasgeleSec() {
	    int sec = ( (int) (Math.random() *3) ) ;
	    Hayvan h = null ;
	    if (sec == 0)  h = new Hayvan();
	    if (sec == 1)  h = new Kartal();
	    if (sec == 2)  h = new Timsah();
	    return h;
    }
    
    public static void main(String args[]) {
	    Hayvan[] h = new Hayvan[3];
	    // diziyi doldur
	    for (int i = 0 ; i < 3 ; i++) {
		    h[i] = rasgeleSec(); //upcasting
	    }
	    // dizi elemanlarini ekrana bas
	    for (int j = 0 ; j < 3 ; j++) {
		h[j].avYakala(); // !Dikkat!
	    }
    }       
}

 

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 diyenler için konuyu biraz daha açalım . rasgeleSec() metodunu incelersek , Math.random() metodunun her seferinde 0 ile 2 arasinda rasgele sayilar ürettiği görürüz.Bu üretilen sayı doğrutulsunda Hayvan objesi Kartal objesi veya Timsah objesi döndürülebilir.Bu sebebten dolayı uygulamamızı her çalıştırdığımızda Hayvan dizinin içersine değişik tipdeki objeler değişik sırada olabileceğini görürüz. Örneğin PolimorfizmIki uygulamamızı üç kere üst üst çalıştırıp çıkan sonuçları inceliyelim ; Uygulamamızı  çalıştırıyorum.

gösterim-2

java  PolimorfizmIki 

Uygulamanın çıktısı aşağıdaki gibidir ; 

Kartal avYakala
Hayvan avYakala
Kartal avYakala

Aynı uygulamamızı tekrardan çalıştırıyorum ;

Timsah avYakala
Timsah avYakala
Hayvan avYakala

Tekrar çalıştırıyorum ;

Timsah avYakala
Hayvan avYakala
Kartal avYakala

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ğlama

Bö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 

class Kedi {
       	
	public void yakalaAv() {
		System.out.println("Kedi sinifi Av yakaladi");
	}
	
}

class Kaplan extends Kedi {

    public static void goster(Kedi k) {
	k.yakalaAv();  // polimorfizm
    }

    public void yakalaAv() {
		System.out.println("Kaplan sinifi Av yakaladi");
	}

    public static void main(String args[] ) {
		Kedi k = new Kedi()  ;
		Kaplan  kp = new Kaplan();
		goster(k); 
		goster(kp); //upcasting
	}       
}

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

class Kedi2 {
       	
	public final void yakalaAv() {
		System.out.println("Kedi sinifi Av yakaladi");
	}
	
}

class Kaplan2 extends Kedi2 {

    public static void goster(Kedi2 k) {
	// k.yakalaAv();  // ! polimorfizm yok ! 
    }

    /* iptal edemez (override)
    public void yakalaAv() {
    	System.out.println("Kaplan sinifi Av yakaladi");
    }
    */
    public static void main(String args[] ) {
		Kedi2 k = new Kedi2()  ;
		Kaplan2  kp = new Kaplan2();
		goster(k);   //upcasting
		goster(kp);  //upcasting
	}       
}

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



class Calisan {
	public String pozisyon = "Calisan";
	public void calis() {
	}
}

class Mudur extends Calisan {

	public Mudur () { // yapilandirici
		pozisyon = "Mudur" ;
	}
	public void calis() {  // iptal etme (override)
		System.out.println("Mudur Calisiyor");
	}
}

class Programci extends Calisan {

	public  Programci() {  // yapilandirici
		pozisyon = "Programci" ;
	}
	public void calis() {	// iptal etme (override)
		System.out.println("Programci Calisiyor");
	}
}

class Pazarlamaci extends Calisan {

	public  Pazarlamaci() {	// yapilandirici
		pozisyon = "Pazarlamaci" ;
	}
	public void calis() {  // iptal etme (override)
		System.out.println("Pazarlamaci Calisiyor");
	}
}


public class IsYeriNon {

	public static  void mesaiBasla(Calisan[] c ) {
		for ( int i = 0 ; i < c.length ; i++ ) {
			if ( c[i].pozisyon.equals("Mudur") ) {
				c[i].calis();
			}
			if ( c[i].pozisyon.equals("Programci") ) {
				c[i].calis();
			}
			if ( c[i].pozisyon.equals("Pazarlamaci") ) {
				c[i].calis();
			}
			//...
		}

	}

	public static void main(String args[]) {
		Calisan[] c = new Calisan[4];
		c[0] = new Calisan();       // yukari dogru cevirim gerekmiyor
		c[1] = new Programci();	    // yukari dogru cevirim (upcasting)
		c[2] = new Pazarlamaci();   // yukari dogru cevirim (upcasting)
		c[3] = new Mudur();         // yukari dogru cevirim (upcasting)
		mesaiBasla(c);
	}
}

Yukarıdaki örneğimizde nesneye yönelik programlamaya yakışmayan davranıştır (OOP) , mesaiBasla() metodunun içersinde yapılmaktadır .Bu metodun içersinde ,  dizi içersindeki elemanların teker teker hangi tipde oldukları kontrol edilip , tiplerine göre calis() metodları çağrılmaktadır.Calisan sınıfından türeteceğim her yeni sınıf için mesaiBasla() metodunun içersinde ayrı bir if-else koşul ifade yazmak gerçekten çok acı verici bir olay olur herhalde . Polimorfizm özelliği bu durumda devreye girerek , bizi bu zahmet veren işlemden kurtarır. Uygulamamızın çıktısı aşağıdaki gibi olur ; 

Programci Calisiyor
Pazarlamaci Calisiyor
Mudur Calisiyor

IsYeriNon.java örneğimizi nesneye yönelik programlama çercevesinde tekrardan yazarsak

ör-IsYeri.java


class Calisan {
    public String pozisyon="Calisan" ;
    public void calis() {}
}

class Mudur extends Calisan {
    
    public Mudur () { // yapilandirici
	 pozisyon = "Mudur" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("Mudur Calisiyor");
    }
}

class Programci extends Calisan {
    
    public  Programci() { // yapilandirici
	pozisyon = "Programci" ;
    }
    public void calis() { // iptal etme (override)
	System.out.println("Programci Calisiyor");
    }
}

class Pazarlamaci extends Calisan {
    
    public  Pazarlamaci() { // yapilandirici
	pozisyon = "Pazarlamaci" ;
    }
    public void calis() { // iptal etme (override)
	System.out.println("Pazarlamaci Calisiyor");
    }
}


public class IsYeri {
    
    public static  void mesaiBasla(Calisan[] c ) {
	for (int i = 0 ; i < c.length ; i++) {
	    c[i].calis(); // !Dikkat!
	}

    }

    public static void main(String args[]) {
	      Calisan[] c = new Calisan[4];
	      c[0] = new Calisan();     // yukari dogru cevirim gerekmiyor
	      c[1] = new Programci();   // yukari dogru cevirim (upcasting)
	      c[2] = new Pazarlamaci(); // yukari dogru cevirim (upcasting)
	      c[3] = new Mudur();       // yukari dogru cevirim (upcasting)
	      mesaiBasla(c);
    }
}

Görüldüğü üzere mesaiBasla() metodu artık tek satır ,bunu polimorfizm kavramı ve tabii  ki geç bağlama özelliğine borçluyuz.Bu sayede artık Calisan sınıfından istediğim kadar yeni sınıf türetebilirim , yani genişletme olayını rahatlıkla yapabilirim hemde mevcut yapıyı bozmadan.Uygulamamızdaki  sınıflara ait  UML diyagramı aşağıdaki gibidir.

Şekil-2

Genişletilebilirlik (Extensibility)

Polimorfizm özelliği sayesinde genişletebilirlik olayı çok basite indirgenmiş bulunmaktadır.Genişletebilirlik olayı nedir diyenler için   gerekli açıklama yaparsak; mevcut hiyerarşiyi kalıtım yolu ile genişletme diyebiliriz.IsYeri.java örneğimizi biraz daha genişletelim; Yeni uygulamamızın adını BuyukIsYeri.java yapalım , Sınıflara ait UML diyagramı aşağıdaki gibidir ; 

Ş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 ;

ör-BuyukIsyeri.java


class Calisan {
    public String pozisyon ;
    public void calis() {}
}

class Mudur extends Calisan {
    
    public Mudur () { // yapilandirici
	 pozisyon = "Mudur" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("Mudur Calisiyor");
    }
}

class GenelMudur extends Mudur {
    
    public GenelMudur () { // yapilandirici
	 pozisyon = "GenelMudur" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("GenelMudur Calisiyor");
    }
    public void toplantiYonet() {  
	System.out.println("GenelMudur toplanti yonetiyor");
    } 
}


class Programci extends Calisan {
    
    public  Programci() { // yapilandirici
	pozisyon = "Programci" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("Programci Calisiyor");
    }
}

class AnalizProgramci extends Programci {
    
    public  AnalizProgramci() { // yapilandirici
	pozisyon = "AnalizProgramci" ;
    }
    public void analizYap() {
	System.out.println("Analiz Yapiliyor");
    }  
}

class SistemProgramci extends Programci {
    
    public  SistemProgramci() { // yapilandirici
	pozisyon = "SistemProgramci" ;
    }
    public void sistemIncele() {
	System.out.println("Sistem Inceleniyor");
    }   
}


class Pazarlamaci extends Calisan {
    
    public  Pazarlamaci() { // yapilandirici
	pozisyon = "Pazarlamaci" ;
    }
    public void calis() { // iptal etme (override)
	System.out.println("Pazarlamaci Calisiyor");
    }
}


class Sekreter extends Calisan {
    
    public  Sekreter() { // yapilandirici
	pozisyon = "Sekreter" ;
    }
    public void calis() { // iptal etme (override)
	System.out.println("Sekreter Calisiyor");
    }
}


public class BuyukIsYeri {
    
    public static  void mesaiBasla(Calisan[] c ) {
	for (int i = 0 ; i < c.length ; i++) {
	    c[i].calis(); // ! Dikkat !
	}
    }

    public static void main(String args[]) {
	      Calisan[] c = new Calisan[7];
	      c[0] = new Calisan();     // yukari dogru cevirim gerekmiyor
	      c[1] = new Programci();   //yukari dogru cevirim (upcasting)
	      c[2] = new Pazarlamaci(); //yukari dogru cevirim (upcasting)
	      c[3] = new Mudur();       //yukari dogru cevirim (upcasting)
	      c[4] = new GenelMudur();  //yukari dogru cevirim (upcasting)
	      c[5] = new AnalizProgramci(); //yukari dogru cevirim (upcasting)
	      c[6] = new SistemProgramci();//yukari dogru cevirim (upcasting)
	      mesaiBasla(c);
    }
}

Yukarıdaki örneğimizde  dikkat edilirse mesaiBasla() metodu hala tek satır. Uygulamamızın çıktısı aşağıdaki gibidir ;

Programci Calisiyor
Pazarlamaci Calisiyor
Mudur Calisiyor
GenelMudur Calisiyor
Programci Calisiyor
Programci Calisiyor

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

class Calisan {
	public String pozisyon = "Calisan";
	public void calis() {}
}

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

abstract void calis() ; // gövdesi olmayan soyut metod 

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

abstract class Calisan {
    public String pozisyon="Calisan" ;
    public abstract  void calis() ;// soyut sinif 
    public void zamIste() { // soyut olmayan sinif
	System.out.println("Calisan zamIste");
    }
}

class Mudur extends Calisan {
    
    public Mudur () { // yapilandirici
	 pozisyon = "Mudur" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("Mudur Calisiyor");
    }
}

class Programci extends Calisan {
    
    public  Programci() { // yapilandirici
	pozisyon = "Programci" ;
    }
    public void calis() {  // iptal etme (override)
	System.out.println("Programci Calisiyor");
    }

    public void zamIste() {  // iptal etme (override)
	System.out.println("Programci Zam Istiyor");
    }
}

class Pazarlamaci extends Calisan {
    
    public  Pazarlamaci() { // yapilandirici
	pozisyon = "Pazarlamaci" ;
    }
    public void calis() { // iptal etme (override)
	System.out.println("Pazarlamaci Calisiyor");
    }
}


public class AbIsYeri {
    
    public static  void mesaiBasla(Calisan[] c ) {
	for (int i = 0 ; i < c.length ; i++) {
	    c[i].calis(); // !Dikkat!
	}

    }

    public static void main(String args[]) {
	      Calisan[] c = new Calisan[3];
	      // c[0] = new Calisan(); // soyut siniflar new ile direk olusturulamazlar
	      c[0] = new Programci();   // yukari dogru cevirim (upcasting)
	      c[1] = new Pazarlamaci(); // yukari dogru cevirim (upcasting)
	      c[2] = new Mudur();       // yukari dogru cevirim (upcasting)
	      mesaiBasla(c);
    }
}

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 .Fakat  zamIste() metodu ise sadece Calisan sınıfından türemiş olan  Programci sınıfı içersinde iptal edilmiştir(override).

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

abstract class Cizim {
    public abstract void noktaCiz(int x , int y) ;// soyut sinif
    
    public void cizgiCiz(int x1 , int y1 , int x2 , int y2) { // soyut olmayan sinif
	   // noktaCiz(x,y); // metodunu kullanarak ekrana cizgi ciz
    }
}


class CepTelefonuCizim extends Cizim {
    public void noktaCiz(int x, int y) { // iptal etme (override)
	// cep telefonu ekrani icin nokta ciz.....
    }
}

class MonitorCizim extends Cizim {
    public void noktaCiz(int x, int y) { // iptal etme (override)
	// Monitor ekrani icin nokta ciz.....
    }
}

public class CizimProgrami {
    public void baslat(int x1 , int y1 , int x2 , int y2) {
	// cep telefonunun ekranina cizgi cizmek icin
	Cizim c1 = new  CepTelefonuCizim();
	c1.cizgiCiz(x1 , y1 ,  x2 , y2);

	// Monitor ekranina cizgi cizmek icin
	Cizim c2 = new  MonitorCizim();
	c2.cizgiCiz(x1 , y1 , x2 , y2 );
    }
}

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ç durumlar

Yapı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

abstract class  Sporcu {
    public abstract void calis();
    public Sporcu() {	// yapilandirici metod
	  System.out.println("calis() cagrilmadan evvel");
	  calis();    // ! Dikkat !
	  System.out.println("calis() cagrildiktan sonra");
    }
}

class Futbolcu extends Sporcu {
   int antraman_sayisi = 4 ;
   public void calis() {
	System.out.println("Futbolcu calis() " + antraman_sayisi );
   }
   public Futbolcu() { // yapilandirici metod
	 System.out.println("Futbolcu yapilandirici" );
	 calis();
   }
}

public class Spor  {
    public static void main( String args[] ) {
	Futbolcu f = new Futbolcu();
	// Sporcu s = new Sporcu(); // ! Hata  soyut sinif !
    }
}

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.

calis() cagrilmadan evvel
Futbolcu calis() 0  --> dikkat 
calis() cagrildiktan sonra
Futbolcu yapilandirici
Futbolcu calis() 4

Uygulama çıktısındaki 2. satır çok ilginçtir.Oluşan olayları adım adım açıklarsak;

  1. Futbolcu sınıfına ait bir obje oluşturulmak istendi ama Futbolcu sınıfı Sporcu sınıfından türetildiği için, ilk önce Sporcu sınıfına ait yapılandırıcı cağrılacaktır.

  2. Sporcu sınıfına ait yapılandırıcının içersinde soyut olan calis() metodu çağrıldı.Soyut olan calis() metodu Futbolcu sınıfının içersinde iptal edildiği için , Futbolcu sınıfına ait olan calis() metodu çağrılacaktır fakat ilkel(primitive) int tipinde olan  antreman_sayisi değişkenine henüz ilk değeri atanmadığından , Java tarafından varsayılan değer (default value) olan 0 sayısı verilmiştir.Buradaki ilginç olan nokta Futbolcu sınıfının içersindeki calis() metodu , Futbolcu sınıfına ait yapılandırıcıdan bile önce çağrılmış olmasıdır (-ki bu istenmeyen bir durumdur).

  3. Ana sınıf olan Sporcu sınıfına ait yapılandırıcının çalışması sona erdikten sonra türemiş sınıf olan Futbolcu sınıfına ait global değişken olan antreman_sayisi değişkeninin ilk değeri verilmiştir. 

  4. En son olarak Futbolcu sınıfına ait yapılandırıcı içerisindeki kodlar çalıştırılarak işlem sona erer.

 

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


class Xmodel {
	public void sesAc() {
		System.out.println("X model televizyon sesAc()");
	}

	public void sesKapa() {
		System.out.println("X model televizyon sesKapa()");
	}

	public void kanalDegistir() {
		System.out.println("X model televizyon kanalDegistir()");
	}
}

class Ymodel extends Xmodel {
	public void sesAc() {  // iptal etme (override)
		System.out.println("Y model televizyon sesAc()");
	}

	public void sesKapa() { // iptal etme (override)
		System.out.println("Y model televizyon sesKapa()");
	}

	public void kanalDegistir() {  // iptal etme (override)
		System.out.println("Y model televizyon kanalDegistir() ");
	}

	public void teleText() {
		System.out.println("Y model televizyon teleText()");
	}

}

public class Televizyon {
	public static void main(String args[]) {

		Xmodel  x_model_kumanda  = new Ymodel(); // yukari dogru cevirim ( upcasting )
		x_model_kumanda.sesAc();
		x_model_kumanda.sesKapa();
		x_model_kumanda.kanalDegistir();
		// x_model_kumanda.teleText() ; !! hata !! , bu kumandanin boyle bir dugmesi yok :)
	}
}

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


class Xmodel {
	public void sesAc() {
		System.out.println("X model televizyon sesAc()");
	}

	public void sesKapa() {
		System.out.println("X model televizyon sesKapa()");
	}

	public void kanalDegistir() {
		System.out.println("X model televizyon kanalDegistir()");
	}
}

class Ymodel extends Xmodel {
	public void sesAc() {  // iptal etme (override)
		System.out.println("Y model televizyon sesAc()");
	}

	public void sesKapa() { // iptal etme (override)
		System.out.println("Y model televizyon sesKapa()");
	}

	public void kanalDegistir() {  // iptal etme (override)
		System.out.println("Y model televizyon kanalDegistir() ");
	}

	public void teleText() {
		System.out.println("Y model televizyon teleText()");
	}

}

public class Televizyon2 {
	public static void main(String args[]) {

	    Object[] ob = new Object[2] ;
	    ob[0] = new Xmodel() ; // yukari dogru cevirim (upcasting)
	    ob[1] = new Ymodel() ; // yukari dogru cevirim (upcasting)

	    for (int i = 0 ; i < ob.length ; i++) {
		  Xmodel x_model_kumanda = (Xmodel) ob[i] ; // asagiya dogru cevirim (Downcasting)
		  x_model_kumanda.sesAc();
		  x_model_kumanda.sesKapa();
		  x_model_kumanda.kanalDegistir();
		  // x_model_kumanda.teleText(); // bu kumanda da boyle bir dugme yok
		  System.out.println("-----------------------------------------------");
	    }

	}
}

Yukarıdaki uygulamamızın çıktısı aşağıdaki gibidir ;

X model televizyon sesAc()
X model televizyon sesKapa()
X model televizyon kanalDegistir()
-----------------------------------------------
Y model televizyon sesAc()
Y model televizyon sesKapa()
Y model televizyon kanalDegistir()
-----------------------------------------------

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

 Xmodel x_model_kumanda = (Xmodel) ob[i] ; // asagiya dogru cevirim (Downcasting)

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 hemen açıklıyalım . Sonuçta bizim iki adet hem Xmodel hemde Ymodel objelerimiz vardı ,biz bunları Object tipine çevirerek yani yukarı doğru çevirim yaparak Object dizisinin içersine yerleştirdik , buraya kadar sorun yok . Fakat Object dizisinin içersinden elemanlarımızı çekerken , bu elemanlarımızın hepsini Xmodel objesine çevirdik yani aşağıya doğru çevirim yapmış olduk (aşağıya doğru çevirim).Biz böyle yapınca Ymodel objemiz arada kaynamış gibi gözükebilir.

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


class Xmodel {
	public void sesAc() {
		System.out.println("X model televizyon sesAc()");
	}

	public void sesKapa() {
		System.out.println("X model televizyon sesKapa()");
	}

	public void kanalDegistir() {
		System.out.println("X model televizyon kanalDegistir()");
	}
}

class Ymodel extends Xmodel {
	public void sesAc() {  // iptal etme (override)
		System.out.println("Y model televizyon sesAc()");
	}

	public void sesKapa() {	// iptal etme (override)
		System.out.println("Y model televizyon sesKapa()");
	}

	public void kanalDegistir() {  // iptal etme (override)
		System.out.println("Y model televizyon kanalDegistir() ");
	}

	public void teleText() {
		System.out.println("Y model televizyon teleText()");
	}

}

public class Televizyon3 {
	public static void main(String args[]) {
		
		Object[] ob = new Object[2] ;
		ob[0] = new Xmodel() ;
		ob[1] = new Ymodel() ;

		for (int i = 0 ; i < ob.length ; i++) {

			Object o = ob[i] ;
			if (o instanceof Ymodel) {	// RTTI
				Ymodel y_model_kumanda = (Ymodel) o ; //artik guvenli
				y_model_kumanda.sesAc();
				y_model_kumanda.sesKapa();
				y_model_kumanda.kanalDegistir();
				y_model_kumanda.teleText() ; 
			} else if (o instanceof Xmodel) { // RTTI
				Xmodel x_model_kumanda = (Xmodel) o ; // artik guvenli 
				x_model_kumanda.sesAc();
				x_model_kumanda.sesKapa();
				x_model_kumanda.kanalDegistir();

			}
		}

	}
}

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 .Cevap:  tabii geri alacağız diyebilirim , geri almanın yolu aşağıya doğru çevirimdir (downcasting).

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;

X model televizyon sesAc()
X model televizyon sesKapa()
X model televizyon kanalDegistir()
Y model televizyon sesAc()
Y model televizyon sesKapa()
Y model televizyon kanalDegistir()
Y model televizyon teleText()

 Bölüm Sonu

//...

Sorular

  1.   (aklınıza gelen bu konular ile ilgili güzel sorular varsa lütfen bildiriniz)

Mail Grubu

Java Kitap Projesinin mail grubuna üye olmak için = java_kitap_projesi-subscribe@yahoogroups.com 

Üyelikten çıkmak için =  java_kitap_projesi-unsubscribe@yahoogroups.com

Sorulanız ve Yorumlarınız için : upux@yahoo.com

 

Bu dökümanın her hakkı saklıdır.

© Copyright 2002