~BöLÜM-7~

Altuğ B. Altıntaş

©2002

Arayüz (Interface)

C++ programlama dilinde olan çoklu kalıtım özelliği Java programlama dilinde yoktur , bunun sebeblerini biraz sonra inceliyeceğiz. Java programlama dilinde çoklu kalıtım desteğinden faydalanmak için arayüz(interface) özelliği kullanılır.

Arayüzler , soyut(abstract) sınıfların bir üst modeli gibi düşünebiliriz, soyut sınıfların içerisinde hem iş yapan hemde hiçbir iş yapmayan sadece birleştirici rol üstlenen gövdesiz metodlarımız (soyut metodlar) vardı. Bu birleştirici rol oynayan metodlar , soyut sınıfdan (abstract class) türetilmiş  alt sınıfların içersinde iptal edilmeleri(override) gerektiğini geçen bölüm incelemiştik. Arayüzlerin içerisinde ise iş yapan herhangi bir metod bulunamaz , arayüzün(interface)  içersinde tamamen gövdesiz metodlar(soyut metodlar)  bulunur. Arayüzler tamamen birleştirici bir rol oynamaları için tasarlanmışlardır. Önemli bir noktayı hemen belirtelim, Arayüzlere ait gövdesiz (soyut) metodlar otomatik olarak public erişim belirliyicisine sahip olurlar ve sizin bunu değiştirme imkanınız yoktur , aynı şekilde arayüzlere ait global değişkenler de public erişim belirliyicisine sahip olurlar artı , bu değişkenler otomatik olarak final ve statik özelliği içerirler ve sizin bunlara yine mudahale etme imkanınız yoktur.

Birleştiricilik

Bölüm-6 daki BüyükIsYeri.java örneğimizi, arayüz özelliği ile baştan yazmadan önce yeni UML diyagramını inceliyelim;

Şekil-1

UML diyagramımızdan görüldüğü üzere ,Calisan arayüzü (interface), birleştirici bir rol oynamaktadır.Calisan arayüzünde tanımlanan ve soyut-gövdesi olmayan calis() metodu, bu arayüzü kullanan tüm sınıfların içersinde iptal edilmiştir (override).UML diyagramımızı Java uygulamasına dönüştürürsek ;

ör-BuyukIsYeri.java 


interface Calisan { // arayuz
       public void calis() ;
}

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

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


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

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

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


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

class Sekreter implements Calisan {
    
    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[6];
	      // c[0] = new Calisan(); ! Hata ! arayüz olusturulamaz
	      c[0] = new Programci(); //yukari dogru cevirim (upcasting)
	      c[1] = new Pazarlamaci(); //yukari dogru cevirim (upcasting)
	      c[2] = new Mudur();  //yukari dogru cevirim (upcasting)
	      c[3] = new GenelMudur();  //yukari dogru cevirim (upcasting)
	      c[4] = new AnalizProgramci(); //yukari dogru cevirim (upcasting)
	      c[5] = new SistemProgramci();//yukari dogru cevirim (upcasting)
	      mesaiBasla(c);
    }
}

Yukarıdaki örneğimiz sizlere ilk etapda  çekici gelmiyebilir, bunun aynısı soyut sınıflarla (abstract class) zaten yapılabiliyordu, arayüz özelliğini neden kullanayım ki diyebilirsiniz.Yukarıdaki örneğimizde arayüz özelliğinin nasıl kullanıldığını inceledik.Arayüz özelliğinin  sağladığı gerçek faydaları birazdan inceliyeceğiz.

Arayüz özelliğinin yazılış tarzını biraz daha yakından inceliyelim .

gösterim-1

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

Örneğin olaylara Mudur sınıfının bakış açısından bakalım.Bu sınıf , Calisan arayüzünun ortak metodlarını iptal etmek(override) istiyorsa , Calisan arayüzüne ulaşması gerekir. Bu ulaşım implements anahtar kelimesi ile gerçekleşir. Mudur sınıfı bir kere Calisan arayüzüne ulaştımı , buradaki gövdesiz metodları(soyut metodları) kesin olarak iptal etmelidir. Uygulamamızın çıktısı aşağıdaki gibidir ;

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

Arayüz(Interface) ve Soyut sınıflar(Abstract classes)

Eğer bir sınıf (soyut sınıflar dahil) bir arayüze (interface) ulaşmak istiyorsa, bunu implements anahtar kelimesi ile gerçekleştirebileceğini belirtmiştik. Ayrıca eğer bir sınıf bir kere arayüze ulaştımı artık onun tüm gövdesiz metodlarını(soyut metodlar) kesin olarak iptal etmesi(override) gerektiğini de belirttik. Peki eğer soyut bir sınıf(abstract sınıf) bir arayüze ulaşırsa , arayüze ait gövdesiz metodları(soyut metodları) kesin olarak , kendi içerisinde mi iptal etmeli ? Bir örnek üzerinde incelersek ;

ör-Karisim.java

interface Hayvan {
    public void avlan() ;
}

abstract class Kedi implements Hayvan {

}

Yukarıdaki örneğimizi derleyebilirmiyiz (compile)   derlense bile çalışma anında (run-time) hata oluşturur mu .Aslında olaylara kavramsal olarak  bakıldığında çözüm yakalanmış olur. Soyut sınıfların amaçlarından biri aynı arayüz özelliğinde (interface) olduğu gibi birleştirici bir oynamaktır. Daha açık bir ifade kullanırsak,hem arayüz(interface) olsun  hemde soyut sınıflar olsun , amaçları  kendilerine ulaşan normal sınıfların , kendilerine ait olan gövdesiz metodlarını (soyut sınıfları) iptal ettirmektir(override) . O zaman yukarıdaki örneğimizde soyut  olan Kedi sınıfının içerisinde , Hayvan arayüzüne(interface) ait gövdesiz (soyut)  avlan() metod  iptal edilmek zorunda değildir diyebiliriz. Daha iyi anlaşıması açısından yukarıdaki örneğimizi biraz daha geliştirelim ama öncesinde UML diyagramını çıkartalım;

Şekil-2

UML diyagramından görüleceği üzere , Kaplan sınıfı , avlan() ve takipEt() metodlarını (gövdesiz-soyut metodlarını) iptal etmek zorundadır.UML diyagramını Java uygulamasına dönüştürürsek;

ör-Karisim2.java

interface Hayvan {
	public void avlan() ;

}

abstract class Kedi implements Hayvan {

	public abstract void takipEt() ;
}

class Kaplan extends Kedi {

	public void avlan() { // iptal etti (override)
		System.out.println("Kaplan avlaniyor...");
	}


	public void takipEt() {	// iptal etti (override)
		System.out.println("Kaplan takip ediyor...");
	}                         

}

Soyut (abstract) olan Kedi sınıfının içerisinde, herhangi bir gövdesiz metod ( soyut metod ) iptal edilmemiştir(override) , iptal edilme(override) işlemlerinin gerçekleştiği yer Kaplan sınıfının içersidir. Soru : Kaplan sınıfı Hayvan arayüzünde (interface) tanımlanmış  soyut olan (gövdesiz) avlan()  metodunu iptal etmek(override) zorunda mı ? Cevap : Evet, çünkü Kaplan sınıfı Kedi sınıfından türetilmiştir. Kedi sınıfı ise Hayvan arayüzüne ulaşmaktadır. Bu sebebten dolayı Kaplan sınıfının içerisinde avlan() metodu iptal edilmelidir (override).

En baştaki sorumuzun cevabı olarak , Karisim.java örneğimiz rahatlıkla derlenebilir (compile) ve çalışma anında (compile-time) herhangi bir çalışma-anı(runtime-exception) istisnasına sebebiyet vermez. İstisnaları(Exception) ilerleyen bölümlerde detaylı bir şekilde inceliyeceğiz.

Arayüz(Interface) ile çoklu kalıtım(Multiple inheritance)

İlk önce çoklu kalıtımın  (multiple inheritance) niye tehlikeli ve Java tarafından kabul görmediğini inceliyelim. Örneğin Sporcu soyut sınıfından türetilmiş iki adet sınıfımız bulunsun , BuzPatenci ve Basketbolcu sınıfları.Bu iki sınıfından türetilen yeni bir sınıf olamaz mı ? örneğin SportmenMehmet sınıfı. Yani SportmenMehmet sınıfı aynı anda hem BuzPatenci , hemde Basketbolcu sınıfından türetilebilir mi ? Java programlama dilinde türetilemez. Bunun sebeblerini incelemeden evvel , hatalı yaklaşımımızı UML diyagramında inceleyelim;

Şekil-3

Peki ama niye ? java niye çoklu kalıtımı bu şekilde desteklemez ? UML diyagramını Java uygulamasına dönüştürelim ;

ör-Spor.java - Hatalı

abstract class Sporcu {
	public abstract  void calis();
}

class BuzPatenci extends Sporcu {
	public void calis() {
		System.out.println("BuzPatenci calisiyor ....") ;
	}
}

class Basketbolcu extends Sporcu {
	public void calis() {
		System.out.println("Basketbolcu calisiyor ....") ;
	}
}

/*
Bu ornegimiz derleme aninda hata alicaktir.
Java, coklu kalitimi desteklemez
*/
class  SportmenMehmet extends BuzPatenci , Basketbolcu {

}

Spor.java derleme anında hata alacaktır. Bu ufak ayrıntıyı belirttikten sonra ,kaldığımız yerden devam edelim.Javanın niye çoklu kalıtımı (multiple inheritance)  desteklemediğini anlamak için aşağıdaki gösterimi inceliyelim.

gösterim-2

Sporcu s = new SportmenMehmet();// yukari dogru cevirim
s.calis(); // ??

Herhangi bir yerden , yukarıdaki gösterimde belirtildiği gibi bir ifade yazılmış olsa , sonuç nasıl olur ? Sporcu tipinde olan değişken , SportmenMehmet objesine bağlanmıştır - bağlanabilir çünkü arada kalıtım ilişkisi vardır . Fakat burada s.calis() ifadesi yazılırsa ,hangi objenin calis() metodu çağrılacaktır ? BuzPatenci objesinin mi ? yoksa Basketbolcu objesinin mi ? Sonuçta , calıs() metodu , BuzPatenci ve Basketbolcu sınıflarının içerisinde iptal edilmiştir(override). Bu sorunun cevabı olmadığı için Java programlama dilinde çoklu kalıtım (multiple inherintance) desteklenmez. Fakat çoklu kalıtımın zararlarından arıtılmış versiyonunu yani arayüz(inheritance) özelliğini kullanarak , diğer dillerinde bulunan çoklu kalıtım desteğini , Java programlama dilinde de bulabiliriz. Peki ama nasıl , diyenler için hemen örneğimizi verelim.Yukarıdaki örneğimizi Java programlama diline uygun bir şekilde baştan yazalım ama öncesinde herzaman ki gibi işe UML diyagramını çizmekle başlayalım;

 

Şekil-4

SportmenMehmet aynı anda hem BuzPatenci hemde Basketbolcu olamaz ama onlara ait özelliklere sahip olabilir. Örneğin BuzPatenci gibi buz üzerinde kayabilir ve Basketbolcu gibi şut atabilir. Yukarıdki UML diyagramını Java uygulamasına dönüştürelim .

ör-Spor2.java

interface BuzUstundeKayabilme  {
	  public void buzUstundeKay();
}

interface SutAtabilme  {
	  public void sutAt(); 
}


class  SportmenMehmet implements  BuzUstundeKayabilme , SutAtabilme {

    public void buzUstundeKay() {
	System.out.println("SportmenMehmet buz ustunde kayiyor");
    }

    public void sutAt() {
	System.out.println("SportmenMehmet sut atiyor");
    }
}

Bu örneğimizde SportmenMehmet, BuzUstundeKayabilme ve SutAtabilme özelliklerine sahip olmuştur. Arayüzler içerisindeki (BuzUstundeKayabilme,SutAtabilme)  gövdesiz (soyut) metodları ( buzUstundeKay() , sutAt() ) , bu arayüzlere erişen sınıf tarafından kesinlikle iptal edilmelidir(overrride) . Eğer iptal edilmez ise , derleme anında (compile-time) Java tarafından gerekli hata mesajı verilir.

Örneğimizden anlaşılabileceği üzere arayüz (interface)  ile soyut (abstract) sınıf arasında büyük fark vardır.En başta kavramsal olarak bir fark vardır. Bu kavramsal fark nedir derseniz hemen açıklayalım; Soyut bir sınıftan türetilme yapıldığı zaman , türetilen sınıf ile soyut sınıf arasında mantıksal bir ilişki olması gerekirdi , örnek vermek gerekirse "Yarasa bir Hayvandır" gibi veya "Müdür bir Çalışandır" gibi.... .Geçen bölümlerde incelediğimiz bir ilişkisi. Fakat arayüzler ile bunlara erişen sınıflar arasında kalıtımsal bir ilişki bulunmayabilir.Yukarıdaki örneğimizi ele alırsak , "SportmenMehmet bir SutAtabilme" veya "SportmenMehmet bir BuzUstundeKayabilme" dersek çok saçma olur. Saçma olur çünkü arada hiç bir kalıtımsal  özellik yoktur.

Arayüzlerin kalıtım (inheritance)  yoluyla genişletilmesi

Bir arayüz başka bir arayüzünden türetilerek yeni özelliklere sahip olabilir böylece arayüzler kalıtım yoluyla genişletilmiş olur. Sınıflarda yapılan türetilme işlemi için nasıl arada mantıksal bir ilişki olması gerekiyorsa , bu kuralın aynısı arayüzler içinde geçerlidir. Olayları daha iyi anlayabilmek için önce UML diyagramını çizip sonrada Java uygulamasını yazalım.

Şekil-5

Avlanabilme arayüzü , DahaHizliKosabilme arayüzünden türemiştir. DahaHizliKosabilme arayüzüde Kosabilme arayüzünde türemiştir. Gerçi bu örneğimizde yok ama bir arayüz (interface)  birden çok arayüzden türetilebilir.   

gösterim-3

interface Avlanabilme extends DahaHizliKosabilme,Kosabilme {
    //..
}

Tabii bu özellik sadece arayüzler için geçerlidir. Yukarıdaki UML diyagramımızı Java uygulamasına dönüştürelim; 

ör-Jaguar.java  

interface Kosabilme {
    public void kos();
}

interface DahaHizliKosabilme extends Kosabilme {
    public void dahaHizliKos();
}

interface Avlanabilme  extends DahaHizliKosabilme {
    public void avlan();
}


class RoadRunner implements Kosabilme , DahaHizliKosabilme {
    public void kos() {
	System.out.println("RoadRunner kosuyor , bip ");
    }
    public void dahaHizliKos() {
	System.out.println("RoadRunner kosuyor , bip bippp");
    }
}
    
public class Jaguar implements Avlanabilme {
    public void avlan() {
	System.out.println("Juguar avlaniyor");
    }
    public void dahaHizliKos() {
	System.out.println("Juguar daha hizli kos");    
    }
    public void kos() {
	System.out.println("Juguar Kosuyor");
    }
}

Jaguar sınıfı Avlanabilme arayüzüne ulaşarak , avlan() , dahaHizliKos() , kos() metodlarını iptal etmek(override) zorunda bırakılmıştır bunun sebebi Avlanabilme arayüzünün DahaHizliKosabilme arayüzünden , DahaHizliKosabilme arayüzüde Kosabilme arayüzünden türemiş olmasıdır.

RoadRunner sınıfı iki arayüze ulaşmıştır DahaHizliKosabilme  ve  Kosabilme. Bu arayüzlerin içerisindeki kos() ve dahaHizliKos() gövdesiz (soyut) metodları RoadRunner tarafından iptal edilmek(override) zorundadır.

Çakışmalar

Arayüzlerin içerisinde dönüş tipleri hariçinde herşeyleri aynı olan gövdesiz (soyut) metodlar varsa, bu durum beklenmedik sorunlara yol açabilir. Yazdıklarımızı Java uygulaması üzerinde gösterirsek ; 

ör-Cakisma.java-Hatalı 


interface A1 {
    public void hesapla();  
}

interface A2 {
    public void hesapla(int d); 
}

interface A3 {
    public int hesapla();
}

class S1 implements A1,A2 { // sorunsuz
    public void hesapla() { //adas metodlar(overloaded)
	System.out.println("S1.hesapla");
    }
    public void hesapla(int d) { //adas metodlar(overloaded)
	System.out.println("S1.hesapla " + d );
    }
}

/* 
! Hatali !, adas metodlar (overloaded)
donus tiplerine gore ayirt edilemez
*/
class S2 implements A1,A3 {
    
    public void hesapla() {
	System.out.println("S2.hesapla");
    } 
    
    /* ! Hata !  
    public int hesapla() {
	System.out.println("S2.hesapla");
	return 123;
    } 
    */
}

Cakisma.java örneğimizi derlersek(compile) , aşağıdaki hata mesajı ile karşılaşırız.

Cakisma.java:27: hesapla() in S2 cannot implement hesapla() in A3; attempting to
 use incompatible return type
found   : void
required: int
class S2 implements A1,A3 {
^
1 error

Bu hatanın oluşma sebebi, A1 ve A3 arayüzlerinin içerisindeki gövdesiz (soyut) metodlarından kaynaklanır. Bu metodların isimleri ve parametreleri aynıdır ama dönüş tipleri farklıdır.

gösterim-4

public void hesapla(); // A1 arayüzüne ait 
public int hesapla();  // A3 arayüzüne ait

Bölüm-3 de incelediğimiz üzere iki metodun adaş metod (overloaded method ) olabilmesi için bu metodların parametrelerinde kesin bir farklılık olması gerekirdi. İki metodun dönüş tipleri dışında herşeyleri aynıysa bunlar adaş metod olamazlar. Olamamalarının sebebi, Javanın bu metodları dönüş tiplerine göre ayırt edememesinden kaynaklanır. 

Arayüzün(Interface) içerisinde değişken tanımlama

Arayüzlerin içerisinde  gövdesiz (soyut) metodların dışında değişkenlerde bulunabilir. Bu değişkenleri uygulamalarımızın içerisinde sabit olarak kullanabiliriz çünkü arayüzün içerisinde tanımlanan bir değişken (ilkel tipte veya Obje tipinde olsun) otomatik olarak hem public erişim belirliyicisine  hemde final ve static özelliğine sahip olur.

ör-AyBul.java

interface Aylar {
	int
	OCAK = 1, SUBAT = 2, MART = 3, 
	NISAN = 4, MAYIS = 5, HAZIRAN = 6, TEMMUZ = 7, 
	AGUSTOS = 8, EYLUL = 9, EKIM = 10,
	KASIM = 11, ARALIK = 12;
}


public class AyBul {

	public static void main(String args[]) {

		int ay =  (int)(Math.random()*13) ;
		System.out.println("Gelen ay = " + ay);
		switch ( ay ) {
			case Aylar.OCAK    : System.out.println("Ocak");break;
			case Aylar.SUBAT   : System.out.println("Subat");break;
			case Aylar.MART    : System.out.println("Mart");break;
			case Aylar.NISAN   : System.out.println("Nisan");break;
			case Aylar.MAYIS   : System.out.println("Mayis");break;
			case Aylar.HAZIRAN : System.out.println("Haziran");break;
			case Aylar.TEMMUZ  : System.out.println("Temmuz");break;
			case Aylar.AGUSTOS : System.out.println("Agustos");break;
			case Aylar.EYLUL   : System.out.println("Eylul");break;
			case Aylar.EKIM    : System.out.println("Ekim");break;
			case Aylar.KASIM   : System.out.println("Kasim");break;
			case Aylar.ARALIK  : System.out.println("Aralik");break;
			default:System.out.println("Tanimsiz Ay"); 
		}
	}
}
Arayüzün içerisinde tanımlanmış değişkenlere ilk değerlerinin verilmesi

Arayüzlerin içerisinde tanımlanmış değişkenlerin ilk değerleri, çalışma anında (run-time)da verilebilir.Aşağıdaki örneğimizi inceliyelim.

ör-Test.java

interface A7 {

    int devir_sayisi  = (int) ( Math.random() *6 ) ;
    String isim = "A7" ;
    double t1 = ( Math.random() * 8 ) ;
}


public class Test {

    public static void main(String args[] ) {

	System.out.println("devir_sayisi = " + A7.devir_sayisi );
	System.out.println("isim = " + A7.isim );
	System.out.println("t1 = " +  A7.t1 );
    }
}

A7 arayüzünün içerisindeki  ilkel (primitive) int tipindeki devir_sayisi ve t1 değişkenlerinin değerlerini derleme anında bilebilmemiz imkansızdır. Bu değerler ancak çalışma anında bilenebilir.

Dikkat edilmesi gereken bir başka husus ise  A7 arayüzünün içerisindeki değişkenlerin ne zaman ilk değerlerini aldıklarıdır. Bir arayüzün içerisindeki değişkenler statik oldukları için , A7 arayüzüne ilk kez erişildiği zaman , A7 arayüzünün içerisindeki tüm değişkenler ilk değerlerini alırlar .

Genel Bakış

Arayüzler (interface) ve soyut (abstract) sınıfların bizlere sağlamak istedigi fayda  nedir ? Aslında ulaşılmak istenen amaç çoklu yukarı doğru çevirimdir (upcasting). Bir sınıfa ait objenin bir çok tipde değişkene bağlanabilmesi , uygulama içerisinde büyük esneklik sağlar.Örnek açıklıyalım....

ör-GenelBakis.java

interface Arayuz1 {
    public void a1() ;
}


interface Arayuz2 {
    public void a2() ;
}


abstract class Soyut1 {
    public abstract void s1();
}

class A extends Soyut1 implements Arayuz1 , Arayuz2 {
    public void s1() { // iptal etme (override)
	System.out.println("A.s1()");
    }
    public void a1() { // iptal etme (override)
	System.out.println("A.a1()");
    }
    public void a2() { // iptal etme (override)
	System.out.println("A.a2()");
    }
}

public class GenelBakis {
    public static void main(String args[]) {
	 Soyut1    soyut_1  =  new A();
	 Arayuz1  arayuz_1  =  (Arayuz1) soyut_1 ;
	 Arayuz2  arayuz_2  =  (Arayuz2) soyut_1 ;
	 // Arayuz2  arayuz_2  =  (Arayuz2)arayuz_1 ; // dogru

	 soyut_1.s1();
	 // soyut_1.a1();  // ! Hata !
	 // soyut_1.a2();  // ! Hata !

	 arayuz_1.a1();
	 // arayuz_1.a2(); // ! Hata !
	 // arayuz_1.s1(); // ! Hata !

	  arayuz_2.a2(); 
	  // arayuz_2.a1(); // ! Hata !
	  // arayuz_2.s1(); // ! Hata !
    }
}

A sınıfı Soyut1 sınıfından türetilmiştir , ayrıca A sınıfı Arayuz1 ve Arayuz2  arayüzlerine (interface) erişmektedir.Daha yakından inceliyelim .

gösterim-5

class A extends Soyut1 implements Arayuz1 , Arayuz2 {

Yukarıdaki gösterim şunu der : A sınıfına ait bir Obje , Soyut1 , Arayuz1 ve Arayuz2  tipindeki değişkenlere bağlanabilir.Anlatılanları UML diyagramına dönüştürürsek.

Şekil-6

Bu örneğimizde görüldüğü üzere ,  A sınıfı ait tek bir obje oluşturulmuştur. Oluşturulan obje farklı değişkenlere bağlanabilmektir. Bir obje , ulaştığı arayüzlerin tipindeki değişkenlere bağlanmasıyla , orijinal halini kaybetmez.A objesi yine A objesidir. 

gösterim-6

Soyut1    soyut_1  =  new A();
Arayuz1  arayuz_1  =  (Arayuz1) soyut_1 ; // tip degisimi 
Arayuz2  arayuz_2  =  (Arayuz2) soyut_1 ; // tip degisimi

Oluşturulan tek bir A sınıfına ait obje, bir çok farklı tipdeki değişkene bağlanabilmektedir.Örneğin yukarıdaki gösterimde, A sınıfına ait objemiz ilk olarak Soyut1 tipindeki değişkene bağlanmıştır. Bağlanabilir çünkü A sınıfı Soyut1 sınıfından türemiştir. Soyut1 tipindeki değişkene bağlı olan A sınıfına ait objemizin sadece s1() metoduna ulaşabiliriz.

Daha sonra Soyut1 değişkeninin bağlı olduğu A sınıfına ait objemizi, Arayuz1 tipindeki değişkenimizi bağlıyoruz ama bu bağlama sırasında  A sınıfına ait objemizin Arayuz1 değişkenine bağlanacağını açık açık söylememiz gerekir. A sınıfına ait objemiz Arayuz1 tipindeki değişkene bağlanırsa , bu objenin sadece a1() metoduna erişilebilir.

Aynı şekilde  Soyut1 değişkenine bağlı olan A sınıfına ait objemizi, Arayuz2 değişkenine bağlıyabiliriz.A sınıfına ait objemiz Arayuz2 tipindeki değişkene bağlanırsa , bu objenin sadece a2() metoduna erişebiliriz. Gösterim-5 deki ifade yerine aşağıdaki gibi bir ifade de  kullanılabilir fakat bu sefer üç ayrı A sınıfına ait obje oluşturmuş oluruz.

gösterim-7

Soyut1  soyut_1  = new A();
Arayuz1 arayuz_1 = new A();
Arayuz2 arayuz_2 = new A();

Görüldüğü üzere , A sınıfına ait üç adet obje oluşturduk ve bu objelerin her birini  farklı tipde değişkenlere bağlayabildik. Bu olay nesneye yönelik tasarımlar yaparken işimize çokca yarayacak bir yaklaşımdır.Bazı durumlarda bir objenin sadece tek veya iki tane metodunun erişebilir olmasını isteyebiliriz. Bu hem güvenlik hemde tasarım açısından faydalıdır. 

Dahili arayüzler (Nested interface)

Bir arayüzün, başka bir arayüzün veya sınıfın içerisinde tanımlanabilir. Bir arayüzün içerisinde tanınmlanan dahili arayüzler, protected , frendly veya  private erişim belirliyicisine sahip olamaz. Örneğimize geçmeden evvel UML diyagramını çizelim.

Sekil-7

Java  uygulamasına geçmeden evvel olaylara kuş bakışı bakalım. UML diyagramımızdan anlaşılacağı üzere, ArayuzA arayüzünün içerisinde iki adet dahili arayüz (nested  interface) tanımlanmıştır. Dışarıdaki iki sınıfımız, dahili olarak tanımlanmış bu iki arayüze erişebilmektedir.

ör-DahiliArayuzTest.java

interface ArayuzA { // aslinda public erisim belirliyicisine sahip
    
    public interface DahiliArayuz1 {
	public void isYap1() ;
    }

    /* //  ! Hata ! 
    protected interface DahiliArayuz2 {
	public void isYap2() ;
    }
    */    
    interface DahiliArayuz3 { // aslinda public erisim belirliyicisine sahip
	public void isYap3() ;
    }

    /* //  ! Hata ! 
    private interface DahiliArayuz4 {
	public void isYap4() ;
    }
    */
}


class Erisim1  implements  ArayuzA.DahiliArayuz1 {
    public void isYap1() {
	System.out.println("Erisim1.isYap1()");
    }
}

class Erisim2  implements  ArayuzA.DahiliArayuz3 {
    public void isYap3() {
	System.out.println("Erisim1.isYap3()");
    }
}

public class DahiliArayuzTest {

    public static void main(String args[]) {
  	Erisim1 e1 = new Erisim1();
	Erisim2 e2 = new Erisim2();
	e1.isYap1();
	e2.isYap3();

    } 
}

Dahili arayüzler , protected, friendly veya  private olamazlar. Dahili arayüzlere erişen sınıflar açısından olaylar aynıdır. Yine bu dahili arayüzlerin içerisindeki gövdesiz(soyut) metodları iptal etmeleri gerekmektedir.Uygulamamızın çıktısı aşağıdaki gibidir ;

Erisim1.isYap1()
Erisim1.isYap3()
Sınıfların içerisinde tanımlanan dahili arayüzler (Nested interface)

Bir arayüz diğer bir arayüzün içerisinde tanımlandığı gibi, bir sınıfın içerisinde de tanımlanabilir. 

ör-SinifA.java

public class SinifA {

    public interface A1 {
	public void ekranaBas();
    }  //interface

    public class DahiliSinif1 implements A1 {
	public void ekranaBas() {
	    System.out.println("DahiliSinif1.ekranaBas()");
	}
    }  // class DahiliSinif1

    protected class DahiliSinif2 implements A1 {
	public void ekranaBas() {
	    System.out.println("DahiliSinif2.ekranaBas()");
	}
    }  // class DahiliSinif2

    class DahiliSinif3 implements A1 {
	public void ekranaBas() {
	     System.out.println("DahiliSinif3.ekranaBas()");
	}
    }  // class DahiliSinif3

    private class DahiliSinif4 implements A1 {
	public void ekranaBas() {
	     System.out.println("DahiliSinif4.ekranaBas()");
	}
    }  // class DahiliSinif4

    public static void main(String args[]) {
	SinifA.DahiliSinif1  sad1 = new SinifA().new DahiliSinif1();
	SinifA.DahiliSinif2  sad2 = new SinifA().new DahiliSinif2();
	SinifA.DahiliSinif3  sad3 = new SinifA().new DahiliSinif3();
	SinifA.DahiliSinif4  sad4 = new SinifA().new DahiliSinif4();

	sad1.ekranaBas();
	sad2.ekranaBas();
	sad3.ekranaBas();
	sad4.ekranaBas();

	SinifB sb = new SinifB();
	sb.ekranaBas();
    }

}

class SinifB implements SinifA.A1{
    public void ekranaBas() {
	System.out.println("SinifB.ekranaBas()");
    }
}

SinifA sınıfının içerisinde tanımlanan A1 arayüzüne, SinifB sınıfından ulaşılabilir. Bir sınıfın içerisinde dahili arayüz tanımlanabildiği gibi dahili sınıfda tanımlanabilir. Bu konuyu az sonra inceliyeceğiz. Bu örneğimizdeki ana fikir, bir sınıfın içerisinde nasıl dahili arayüzün oluşturulduğu ve bu dahili arayüzün, dahili sınıf olsun veya dışarıdan başka bir sınıf tarafından olsun nasıl erişilebildiğini göstermektir.

Dahili sınıflar (Inner classes)

Dahili sınıflar JDK 1.1 ile gelen bir özelliktir. Bu özellik sayesinde  bir sınıfı diğer bir sınıfın içersinde tanımlayabiliriz. Böylece mantıksal bir bütünü oluşturan  bir çok sınıf tek bir çatı alında toplanabilir.Dahili sınıfları yapısal olarak 3 gruba ayırabiliriz.

  • Dahili üye sınıflar 

  • Yerel sınıflar (Local classes)

  • İsimsiz sınıflar (Anonymous classes)

Dahili üye sınıflar  

Bir sınıfın içerisinde, başka bir sınıf tanımlamamız mümkündür. 

gösterim-8

class CevreliyiciSinif {
	
	class DahiliSinif
	{
		//....
	}

	//...
} 

Başka bir sınıfın içerisinde tanımlanan bu sınıfa  dahili üye sınıf denir. Dahili sınıfları, çevreliyici sınıfların içerisinde kullanmak, geçen bölümlerde incelediğimiz komposizyondan yönteminden farklıdır

Dahili üye sınıfları, tek başlarına bağımsız sınıflar gibi düşünebilirsiz.Örnek üzerinde incelersek . 

ör-Hesaplama.java


public class Hesaplama {
    

    public class Toplama { //Dahili uye sinif 
                public  int toplamaYap(int a, int b) {
		    return a+b ; 
		}
    }  // class Toplama

   public static void main(String args[]) {
       Hesaplama.Toplama ht = new Hesaplama().new Toplama() ;
       int sonuc = ht.toplamaYap(3,5);
       System.out.println("Sonuc = " + sonuc );
   }

} // class Hesapla

Hesaplama sınıfının içerisinde tanımlanmış Toplama sınıfı, dahili üye sınıfdır. Hesaplama sınıfı ise çevreliyici sınıfdır. Toplama sınıfını oluşturmak için önce Hesaplama sınıfını oluşturmamız gereklidir.

gösterim-9

Hesaplama.Toplama ht = new Hesaplama().new Toplama() ;

ht değişkeni Toplama sınıfı tipindedir. Artık bu değişkeni kullanarak Toplama sınıfına ait toplamaYap() metoduna ulaşabiliriz.Uygulamamızın çıktısı aşağıdaki gibi olur ;

Sonuc = 8
Dahili üye sınıflar ve erişim

Dahili üye sınıflar  public, friendly, protected veya private olarak tanımlanabilirler böylece dahili üye sınıflarımıza olan  erişimi kısıtlamış oluruz. Dikkat edilmesi gereken diğer bir husus ise bir dahili üye sınıf private olsa dahi, çevreliyici sınıf içerisindenki tüm metodlar tarafından erişilebilir olmasıdır.  Bu kısıt ancak başka sınıflar için geçerlidir.

ör-Hesaplama1.java


public class Hesaplama1 {


	public class Toplama { // Dahili uye sinif -  public 
		public  int toplamaYap(int a, int b) {
			return a + b ; 
		}
	}  // class Toplama

	protected  class Cikartma {  // Dahili uye sinif - protected 
		public  int cikartmaYap(int a, int b) {
			return a - b ; 
		}
	}  // class Cikartma

	class Carpma { // Dahili uye sinif - friendly
		public  int carpmaYap(int a, int b) {
			return a * b ; 
		}
	}  // class Carpma

	private class Bolme { // Dahili uye sinif - private 
		public  int bolmeYap(int a, int b) {
			return a / b ; 
		}
	}  // class Bolme


	public static void main(String args[]) {
		
		Hesaplama1.Toplama  ht  = new Hesaplama1().new Toplama() ;
		Hesaplama1.Cikartma hck = new Hesaplama1().new Cikartma() ;
		Hesaplama1.Carpma   hcp = new Hesaplama1().new Carpma() ;
		Hesaplama1.Bolme    hb  = new Hesaplama1().new Bolme() ;

		int sonuc1 = ht.toplamaYap(10,5);
		int sonuc2 = hck.cikartmaYap(10,5);
		int sonuc3 = hcp.carpmaYap(10,5);
		int sonuc4 = hb.bolmeYap(10,5);

		System.out.println("Toplama  Sonuc = " + sonuc1 );
		System.out.println("Cikartma Sonuc = " + sonuc2 );
		System.out.println("Carpma   Sonuc = " + sonuc3 );
		System.out.println("Bolme    Sonuc = " + sonuc4 );
	}


} // class Hesaplama

Hesaplama1 sınıfımızın içerisinde toplam 4 adet dahili üye sınıf mevcuttur. public erişim beliryicisine sahip Toplama dahili üye sınıfı, protected erişim belirliyicisen sahip Cikartma dahili üye sınıfı, friendly erişim belirliyicisine sahip Carpma dahili üye sınıfı ve private erişim belirliyicise sahip Bolme üye dahili sınıfı. Hesaplama1 sınıfı, bu 4 adet dahili üye  sınıfın çevreliyici sınıfıdır. Çevreliyici olan Hesaplama1 sınıfının statik olan main()  metoduna dikkat edersek, bu metodun içerisinde tüm (private dahil) dahili üye sınıflara erişilebildiğini görürüz.Bunun sebebi, main() metodu ile tüm dahili üye sınıfların aynı çevreliyici sınıfın içerisinde olmalarıdır. Uygulamamızın çıktısı aşağıdaki gibidir:

Toplama Sonuc = 15
Cikartma Sonuc = 5
Carpma Sonuc = 50
Bolme Sonuc = 2

Yukarıdaki örneğimizin yeni bir versiyonunu yazıp, üye dahili sınıflar ile bunlara ait erişim belirliyicilerin nasıl işe yaradıklarını inceliyelim.

ör-Hesaplama2Kullan.java


class Hesaplama2 {  

	public class Toplama2 { // Dahili uye sinif  -  public 
		public  int toplamaYap(int a, int b) {
			return a + b ; 
		}
	}  // class Toplama2

	protected  class Cikartma2 {	// Dahili uye sinif - protected 
		public  int cikartmaYap(int a, int b) {
			return a - b ; 
		}
	}  // class Cikartma2

	class Carpma2 { // Dahili uye sinif - friendly
		public  int carpmaYap(int a, int b) {
			return a * b ; 
		}
	}  // class Carpma2

	private class Bolme2 { // Dahili uye sinif - private 
		public  int bolmeYap(int a, int b) {
			return a / b ; 
		}
	}  // class Bolme2   

} // class Hesaplama2


public class Hesaplama2Kullan {
    public static void main(String args[]) {
		
		Hesaplama2.Toplama2  ht   =   new Hesaplama2().new Toplama2() ;
		Hesaplama2.Cikartma2 hck = new Hesaplama2().new Cikartma2() ;
		Hesaplama2.Carpma2   hcp = new Hesaplama2().new Carpma2() ;
		// Hesaplama2.Bolme3     hb =   new Hesaplama2().new Bolme2() ; // ! Hata ! 

		int sonuc1 = ht.toplamaYap(10,5);
		int sonuc2 = hck.cikartmaYap(10,5);
		int sonuc3 = hcp.carpmaYap(10,5);
	        // int sonuc4 = hb.bolmeYap(10,5); // ! Hata ! 

		System.out.println("Toplama  Sonuc = " + sonuc1 );
		System.out.println("Cikartma Sonuc = " + sonuc2 );
		System.out.println("Carpma   Sonuc = " + sonuc3 );
		
	}

}

Hesaplama2 sınıfımız, toplam 4 adet olan dahili üye sınıflarının çevreliyicisidir. Dahili üye sınıflarımızı ve onlara ait erişim belirliyicilerini inceliyelim :

  • Toplama2 sınıfı, public erişim beliryicisine sahip olan dahili üye sınıfıdır.

  • Cikartma2 sınıfı, protected erişim belirliyicisine sahip olan dahili üye sınıfıdır.

  • Carpma2 sınıfı, friendly erişim belirliyicisine sahip olan dahili üye sınıfıdır.

  • Bolme2 sınıfı, private erişim belirliyicisine sahip olan dahili üye sınıfıdır.

Hesaplama2Kullan sınıfının statik olan main() metodunun içerisinden, Hesaplama2 sınıfının içerisindeki dahili üye sınıflara erişilebilir mi ? Erişilebilir ise hangi erişim belirliyine sahip olan dahili üye sınıflara erişilebilir? 

Normalde bir sınıf private veya protected erişim beliryicisine sahip olamaz ancak dahili üye sınıflar private veya protected erişim belirliyicise sahip olabilir. Hesaplama2Kullan sınıfı, Hesaplama2 sınıfı ile aynı paket içersinde (varsayılan paket) oldukları için, Hesaplama2Kullan sınıfı, Hesapla2 sınıfının içerisinde tanımlanmış olan public, protected ve friendly erişim belirliyicilerine sahip olan dahili üye sınıflara erişebilir ama private erişim belirliyicisine sahip olan Bolme dahili üye sınıfına erişemez.Uygulamamızın çıktısı aşağıdaki gibidir ; 

Toplama Sonuc = 15
Cikartma Sonuc = 5
Carpma Sonuc = 50
Dahili üye sınıflar ve bunları çevreleyen sınıflar arasındaki ilişki 

Dahili üye sınıflar, içerisinde bulundukları çevreliyici sınıfların tüm değişkenlerine (statik veya değil-private dahil) ve metodlarına (statik veya değil-private dahil ) erişebilirler.

ör-Hesaplama3.java


public class Hesaplama3 {
    
    private int sabit1  = 2 ; 
    private static int sabit2 = 1 ;


    public class Toplama3 { //Uye dahili sinif 
                public  int toplamaYap(int a, int b) {
		    return  (a+b) + sabit1  ; // dikkat
		}
    }  // class Toplama3

    public class Cikartma3 { //Uye dahili sinif 
                public  int cikartmaYap(int a, int b) {
		    dekontBilgileriGoster();   // dikkat
		    return  (a-b) -  sabit2  ; // dikkat
		}
    }  // class Cikartma3

    
    private void  dekontBilgileriGoster() {
	System.out.println("Dekont Bilgileri Gosteriliyor");

    }

    public void ekranaBas(int a , int b ) {
	     int sonuc = new Toplama3().toplamaYap(a,b);
	     System.out.println("Sonuc  =  " + a +" + " + b  + " +  sabit1  = " + sonuc);
    }



   public static void main(String args[]) {
       
       Hesaplama3 h3 = new Hesaplama3();
       h3.ekranaBas(10,5);

       // Toplama islemi
       Hesaplama3.Toplama3 ht3 = h3.new Toplama3() ;
       int sonuc = ht3.toplamaYap(11,6);
       System.out.println("Sonuc  =  11 + 6 + sabit1  =  " + sonuc );
       
       // Cikartma islemi 
	 Hesaplama3.Cikartma3 hc3  = h3.new Cikartma3();
	 int sonuc1 = hc3.cikartmaYap(10,5);
	 System.out.println("Sonuc  =  10 - 5 -  sabit2  =  " + sonuc1);
     }


} // class Hesaplama3

Hesaplama3 sınıfının içerisinde iki adet dahili üye sınıf bulunmaktadır, bunlar Toplama3 ve Cikartma3 sınıflarıdır.Toplama3 dahili üye sınıfı, Hesaplama3 sınıfı içerisinde global olarak tanımlanmış ilkel (primitive) int tipindeki ve private erişim belirliyicisine sahip olan sabit1 değişkenine ulaşmaktadır. Toplama3 dahili üye sınıfı, sabit1 değişkenini kullanırken sanki kendi içerisinde tanımlanmış bir değişkenmiş gibi, hiç bir belirteç kullanmamaktadır.

Aynı şekilde Cikartma3 dahili üye sınıfı, Hesaplama3 sınıfının içerisinde statik olarak tanımlanmış, private erişim belirliyicisine sahip ilkel(primitive) int tipindeki sabit2 değişkenine ve private olarak tanımlanmış dekontBilgileriGoster() metoduna direk olarak ulaşabilmektedir.

Hesaplama3 sınıfının, obje metodu olan (-bu metodun kullanılabilmesi için Hesaplama3 sınıfına ait bir obje oluşturmak gerekir) ekranaBas(), iki adet parametre alıp, geriye hiçbirşey döndürmez(void). Bu metodun içerinde Toplama3 dahili üye sınıfına ait obje oluşturularak, bu dahili üye sınıfın toplamaYap() metodu çağrılmaktadır. Toplama3 dahili üye sınıfının toplamaYap() metodundan dönen cevap, ekranaBas() metodunun içerisinde ekrana bastırılır.

Dikkat edilmeye değer diğer bir husus ise sadece bir adet çevreliyici sınıfa ait obje oluşturup, Bu objeye bağlı değişkeni kullanarak diğer dahili üye sınıfları oluşturulmasıdır.Olaylara daha yakından bakarsak ;

gösterim-10

Hesaplama3 h3 = new Hesaplama3();
Hesaplama3.Toplama3  ht3  = h3.new Toplama3() ;
Hesaplama3.Cikartma3 hc3  = h3.new Cikartma3();

Sadece bir adet Hesaplama3 sınıfına ait obje oluşturduk. Bu objeye bağlı değişkeni kullanarak (h3), diğer dahili üye sınıflara ait objeleri oluşturabiliyoruz. Buradaki anafikir, her dahili üye sınıfına ait bir obje  oluşturmak için, yeni bir çevreliyici sınıfa ait obje  oluşturma zorunluluğumuz olmadığıdır. Uygulamamızın çıktısı aşağıdaki gibidir ; 

Sonuc = 10 + 5 + sabit1 = 17
Sonuc = 11 + 6 + sabit1 = 19
Dekont Bilgileri Gosteriliyor
Sonuc = 10 - 5 - sabit2 = 4
 
Statik dahili üye sınıflar 

Statik (static) olarak tanımlanmış dahili üye sınıflar, normal dahili üye sınıfladan farklıdırlar. Bu farklılıklar şöyledir;

  • Statik dahili üye sınıfına ait obje oluşturmak için, onu çevreleyen sınıfa ait obje oluşmak zorunda değilizdir.

  • Statik dahili üye sınıflar, kendilerini çevreleyen sınıfa ait bağlantıyı (-this-) kaybederler . 

Statik dahili üye sınıflar, onları çevreleyen üst sınıfa ait  global değişkenlere (statik veya değil) ve metodlara (statik veya değil) direk ulaşım şansını kaybeder. Bunun sebebi, kendisini çevreleyen sınıf ile arasındaki bağı kopartmış olmasıdır. Buraya kadar ifade ettiklerimiz örnek üzerinde inceleyelim, ama işe öncelikle UML diyagramını çizmekle başlayalım.

Şekil-8

Hesaplama4 sınıfının içerisinde, 2 adet dahili üye sınıf oluşturacağız. Fakat bu dahili üye sınıflardan birini statik olarak tanımlıyacağız. Bu örneğimizde statik olarak tanımlanan dahili üye sınıf, Toplama4 sınıfıdır. Toplama4 sınıfına ait bir obje oluşturmak istersek, öncesinde Hesaplama4 sınıfına ait bir obje oluşturmak zorunda değilizdir.UML diyagramını Java uygulamasına dönüştürelim ;

ör-Hesaplama4.java


public class Hesaplama4 {

	int sabit  = 2 ; 
	private int ozelsabit = 1 ;

	public static class Toplama4 { // Statik uye dahili sinif  
		static int toplam ; // dogru
		int sonuc ;	// dogru
		public  int toplamaYap(int a, int b) {
			// return  (a+b)  + sabit  ;  ! Hata ! 
			sonuc = toplam = a+b ;
			return sonuc    ; 
		}

		public void dekontOlustur() {
			/*  -sabit- degiskenine  ve 
				-ekranaBas() metoduna ulasabilmek icin
			   Hesaplama4 sinifina ait obje olusturmamiz gerekir. 
			*/
			Hesaplama4 hs4 = new Hesaplama4(); //dikkat
			int a =  hs4.ozelsabit ; // dogru
			hs4.ekranaBas() ;	//dogru
			System.out.println("Dekont olusturuyor = " +  hs4.sabit + " - "  +a  );
		}
	}  // class Toplama4


	public class Cikartma4 { //Uye dahili sinif 

		int sonuc ;
		// static int sonuc1 ; // ! hata !  
		public  int cikartmaYap(int a, int b) {
			ekranaBas();  // dikkat
			sonuc = (a-b) -  ozelsabit ;
			return sonuc; // dikkat
		}
	}  // class Cikartma4

	private void ekranaBas() {
		System.out.println("Hesaplama4.ekranaBas()");
	}


	public static void main(String args[]) {
		// Hesaplama4.Toplama4 ht = new Hesaplama4().new Toplama4() ; // ! Hata ! 
		Toplama4  tp4  =  new Toplama4();
		tp4.dekontOlustur();
		int sonuc = tp4.toplamaYap(10,5);
		System.out.println("Sonuc  =  10 + 5    " + sonuc );
	}
} // class Hesaplama4

class Hesaplama4Kullan {

	public static void main(String args[]) {
		// Hesaplama4.Toplama4 ht = new Hesaplama4().new Toplama4() ; // ! Hata ! 
		Hesaplama4.Toplama4  tp4  =  new Hesaplama4.Toplama4();
		int sonuc = tp4.toplamaYap(10,5);
		System.out.println("Sonuc  =  10 + 5    " + sonuc );
	}
}  // class  Hesaplama4Kullan


Statik dahili üye sınıf olan Toplama4 sınıfını yakın takibe alıp, neleri nasıl yaptığını inceliyelim. Toplama4 statik dahili sınıfının içerinsinde statik global değişken tanımlayabiliz - Statik olmayan dahili üye sınıfların içerisinde statik global değişken tanımlanamaz.

Toplama4 statik dahili üye sınıfının, toplamaYap() metodunun içerisinde, Hesaplama4 sınıfına ait global olarak tanımlamış  ilkel (primitive) int tipindeki sabit değişkenine direk erişilemez. Statik dahili üye sınıflar ile bunları çevreleyen sınıflar arasında this bağlantısı yoktur. Eğer statik dahili üye sınıfın içerisinden, onu çevreleyen sınıfa ait bir değişken veya metod çağrılmak isteniyorsa, bu bizzat ifade edilmelidir.Aynı Toplama4 statik dahili üye sınıfına ait dekontOlustur() metodunun içerisinde yapıldığı gibi.

dekontOlustur() metodunun içerisinde, Hesaplama4 sınıfına ait obje oluşturulmadan, sabit , ozelsabit  değişkenlerine ve ekranaBas() metoduna ulaşamazdık. Buradaki önemli nokta, dahili üye sınıf statik olsa bile, kendisine çevreleyen sınıfın private erişim belirliyicisi sahip olan değişkenlerine (statik veya değil) ve metodlarına (statik veya değil) erişebilmesidir  .

Hesaplama4 sınıfının statik olan main() metodunun içerisinde, Toplama4 statik dahili üye sınıfına ait objenin nasıl oluşturulduğuna dikkat edelim. Toplama4 statik dahili üye sınıfına ait obje oluştururken, onu çevreleyen sınıfa ait herhangi bir obje oluşturmak zorunda kalmadık.

Son olarak Hesaplama4Kullan sınıfında statik olarak tanımlanan main() metodunun içerisindeki olayları inceliyelim. Başka bir sınıfın içerisinde statik dahili üye sınıfı ulaşmak için, sadece tanımlama açısından, dahili üye sınıfı çevreleyen sınıfın ismi kullanılmıştır. Mantıklı olanda budur, statik de olsa sonuçta ulaşılmak istenen dahili üye bir sınıfdır.

Elimizde iki adet çalıştırılabilir sınıf mevcutur (-main() metodu olan). Hesaplama4 sınıfını çalıştırdığımızda(java Hesaplama4), sonuç aşağıdaki gibi olur ; 

Hesaplama4.ekranaBas()
Dekont olusturuyor = 2 - 1
Sonuc = 10 + 5 = 15

Eğer Hesaplama4Kullan sınıfını çalıştırsak (java Hesaplama4Kullan), sonuç aşağıdaki gibi olur;

Sonuc = 10 + 5 = 15
 
Statik dahili üye sınıflar ve statik metodlar

Statik dahili üye sınıfların içerisinde statik değişkenler bulunduğu gibi, statik metodlarda bulunabilir.Eğer statik dahili üye sınıfı içerisinde, statik bir metod oluşturulmuş ise, bu metodu çağırmak için ne statik dahili üye sınıfına ne de onu çevreleyen sınıfa ait herhangi bir obje oluşturmak gerekmez.

ör-Hesaplama5.java

public class Hesaplama5 {
	
	private static int  x = 3 ;

	public static class Toplama5 { // Statik uye dahili sinif  
		static int toplam ; // dogru
		int sonuc ;	// dogru
		public  static int toplamaYap(int a, int b) {
			// sonuc = a+b + x ;  // ! Hata !
		      toplam = a + b + x ;
			return toplam    ; 
		}
	} // class Toplama5

	public static void main(String args[]) {
		int sonuc = Hesaplama5.Toplama5.toplamaYap(16,8); // dikkat
		System.out.println("Sonuc  =  16 + 8  =  " + sonuc );
	}
} // class Hesaplama5

Toplama5 statik dahili üye sınıfının, statik olan toplamaYap() metodu, Hesaplama5 çevreliyici sınıfına ait ilkel(primitive) int tipinde tanımlanmış x değişkenine ulaşabilir. Bunun sebebi x değişkeninde statik olarak tanımlanmış olmasıdır. main() metodunun içerisinde, toplamaYap() metodunun çağrılışına dikkat ederseniz, ne Hesaplama5 sınıfına ait obje, ne de Toplama5 statik dahili üye sınıfına ait bir obje oluşturulmuştur. Uygulmamızın çıktısı aşağıdaki gibidir;

Sonuc = 16 + 8 = 27
 
Statik ve final değişkenler

Statik olmayan dahili üye sınıfların içerisinde, statik değişkenler ve metodlar tanımlanamaz ama "statik ve final" değişkenler tanımlanabilir. Bir değişkenin hem statik hemde final olması demek, onun SABİT olması anlamına geldiği için, Statik olmayan dahili üye sınıfların içerisinde statik ve final değişkenler kullanılabilir.

ör-StatikFinal.java


// Hatali 
class CevreliyiciSinif1 {

	class DahiliSinif1 {// Dahili uye siniflar
		// static int x = 10 ; // Yanlis!
	}

}

// Dogru
class CevreliyiciSinif2 {
	
	class DahiliSinif2 {
		int x; // Dogru
	}

}


// Dogru
class CevreliyiciSinif3 { 
	
	class DahiliSinif3 {
		static final int x = 0;  // Dogru
	}

}

 

Dahili üye sınıflar ve yapılandıcılar (Constructors)

Dahili üye sınıfların yapılandırıcıları olabilir.

ör-BuyukA.java


public class BuyukA {

	     public class B{ // yapilandirici
		 public B() {
		     System.out.println("Ben B sinifi ");
		  }
	     } // class B


	     public BuyukA() {
		  System.out.println("Ben BuyukA sinifi ");
	     }

	     public static void main(String args[]) {
		 BuyukA ba = new BuyukA();
	     }
}

Dahili üye sınıfını çevreleyen sınıfa ait bir obje oluşturulduğu zaman,  dahili üye sınıfına ait bir obje otomatik oluşturulmaz. Yukarıdaki örneğimizde sadece BuyukA sınıfına ait bir obje oluşturulmuştur ve bu yüzden sadece BuyukA sınıfına ait yapılandırıcı çağrılacaktır. Eğer dahili üye sınıf olan B sınıfına ait yapılandırıcının çağrılmasını isteseydik, main() metodunun içerisine : "BuyukA.B ab = new BuyukA.newB() " dememiz gerekirdi.

İç içe dahili üye sınıflar 

Bir sınıfın içerisinde dahili üye sınıf tanımlıyabilirsiniz. Tanımlanan bu dahili üye sınıfın içerisinde, yine bir dahili üye sınıf tanımlıyabilirsiniz.. bu böyle sürüp gidebilir.....

ör-Abc.java


public class Abc {

    public Abc() {  // Yapilandirici (Constructor)
        System.out.println("Abc objesi olusturuluyor");
    }

    public class Def {

        public Def() { // Yapilandirici (Constructor)
            System.out.println("Def objesi olusturuluyor");
        }

        public class Ghi {

            public Ghi() { // Yapilandirici (Constructor)
                System.out.println("Ghi objesi olusturuluyor");
            }  

        }  // class Ghi    

    } //class Def    


    public static void main( String args[] ) {
        Abc.Def.Ghi  ici_ice = new Abc().new Def().new Ghi();
    }

}  // class Abc

Bu örneğimizde iç içe geçmiş üç adet sınıf vardır. Uygulamamızın çıktısı aşağıdaki gibi olur :

Abc objesi olusturuluyor
Def objesi olusturuluyor
Ghi objesi olusturuluyor

 

Soyut (abstract) dahili üye sınıflar

Dahili üye sınıflar, soyut(abstract) sınıf olarak tanımlanabilir. Bu soyut dahili üye sınıflardan türeyen sınıflar, soyut dahili üye sınıfların içerisindeki gövdesiz(soyut) metodları iptal etmeleri(override) gerekmektedir.Örneğimize geçmeden evvel, UML diyagramını inceliyelim.

 Şekil-9

Hayvan sınıfının içerisinde soyut (abstract) dahili üye sınıf olarak tanımlanmış Kus sınıfı iki adet gövdesiz (soyut-abstract ) metodu olsun, uc() ve kon(). Kartal sınıfı, soyut dahili üye sınıf olan Kus sınıfından türetilebilir.

ör-HayvanKartal.java


class Hayvan {

    abstract class Kus {  
        public abstract void uc ();
        public abstract void kon();
    }

    public void avlan() {
       System.out.println("Hayvan avlaniyor...");
    }
}

class Kartal extends Hayvan.Kus {
    public void uc() {
       System.out.println("Kartal Ucuyor...");
    }
    public void kon() {
        System.out.println("Kartal Konuyor...");
    }

    
    // public Kartal() {  } // ! Hata !

    public Kartal(Hayvan hv) {  
        hv.super(); //Dikkat
    }

    public static void main(String args[]) {
        Hayvan h = new Hayvan();  //Dikkat
        Kartal k = new Kartal(h);
        k.uc();
        k.kon();
    }
     
}

Kartal sınıfının içerisinde, soyut (abstract) dahili üye sınıf olan Kus sınıfının, gövdesiz(soyut) olan iki metodu iptal edilmiştir (override). Olayları sırası ile inceliyelim, Kartal sınıfına ait bir obje oluşturulmak istense bunun öncesinde Kus sınıfına ait bir objenin oluşturulması gerekir çünkü Kartal sınıfı Kus sınıfından türetilmiştir. Buraya kadar sorun yok, fakat asıl kritik nokta Kus sınıfının dahili üye sınıf olmasıdır. Daha açık bir ifade ile, eğer Kus sınıfına ait bir obje oluşturulacaksa, bunun öncesinde elimizde Kus sınıfının çevreliyici sınıfı olan Hayvan sınıfına ait bir obje bulunması zorunluluğudur. Kus sınıfı statik dahili üye sınıf olmadığından, Hayvan sınıfına bağımlıdır.Uygulamamızın çıktısı aşağıdaki gibidir ;

Kartal Ucuyor...
Kartal Konuyor...

Kartal sınıfının statik olarak tanımlanmış main() metodunun içerisine dikkat edersek, önce Hayvan sınıfına ait bir obje sonrada Kartal sınıfına ait bir obje oluşturduk. Daha sonra Hayvan sınıfı tipinde değişken kabul eden, Kartal sınıfının yapılandırıcısına, bu değişkeni pasladık. Kartal sınıfına ait yapılandırıcının içerisinde super() anahtar kelimesi ile, Hayvan sınıfının varsayılan yapılandırıcısını çağrılmıştır.

gösterim-11

CevreliyiciSinif.super() ; 

Eğer Kus sınıfı, statik dahili üye sınıfı yaparsak, super() anahtar kelimesini kullanmak zorunda kalmayız. Bunun sebebi, statik olan dahili üye sınıfların onları çevreliyen sınıflara bağımlı olmamasıdır.Yukarıdaki örneğimizi bu anlattıklarımıza uydurursak ; 

ör-HayvanKartal1.java


class Hayvan1 {

    static abstract class Kus1 {  
        public abstract void uc ();
        public abstract void kon();
    }

    public void avlan() {
       System.out.println("Hayvan avlaniyor...");
    }
}

class Kartal1 extends Hayvan1.Kus1 {
    public void uc() {
       System.out.println("Kartal1 Ucuyor...");
    }
    public void kon() {
        System.out.println("Kartal1 Konuyor...");
    }

    
     public Kartal1() {  } // dogru

    

    public static void main(String args[]) {
        Kartal1   k1 = new Kartal1();
        k1.uc();
        k1.kon();
    }
     
}

Yukarıdaki örneğimizden görüldüğü üzere, artık Kus sınıfına ait bir obje oluşturmak istersek, bunun hemen öncesinde Hayvan sınıfına ait bir obje oluşturmak zorunda değilizdir bunun sebebi, Kus sınıfının statik dahili üye sınıfı olmasından kaynaklanır.Uygulamamızın çıktısı aşağıdaki gibidir ;

Kartal1 Ucuyor...
Kartal1 Konuyor...
Türetilebilen dahili üye sınıflar 

Dahili üye sınıflar, aynı normal sınıflar gibi başka sınıflardan türetilebilirler.Böylece diğer dillerde olan çoklu kalıtım desteğinin bir benzerini Java programlama dilinde de bulabiliriz. Dahili sınıfların varoluş sebeblerini biraz sonra detaylı bir şekilde inceliyeceğiz. Örneğimize geçmeden evvel, UML diyagramımızı inceliyelim;

Şekil-10

Dahili üye sınıf olan SuperMotor sınıfı, Motor sınıfından türetilmiştir. UML diyagramını Java uygulamasını dönüştürüp, olayları daha iyi kavrayalım.

ör-YarisArabasi.java



class Motor {
    
    public void calis() {
        System.out.println("Motor Calisiyor");
    }

    public void dur() {
        System.out.println("Motor Durdu");
    }

}


public class YarisArabasi {

    public void hizYap() {
        System.out.println("YarisArabasi hiz yapiyor");
    }

    public class SuperMotor extends Motor {
        
        public void calis() { // iptal etti (override)
            System.out.println("SuperMotor Calisiyor");

        }
        public void dur() { // iptal etti (override)
            System.out.println("SuperMotor Durdu");

        }
    }
}

Dahili üye sınıflar, başka sınıflardan türetilebildiği gibi arayüzlere erişip, bunların  içlerindeki gövdesiz(soyut) metodları iptal edebilir(overrride), aynı normal sınıflar gibi...

 

Yerel sınıflar (Local classes)

Yerel sınıflar, yapılandırıcıların(constructors), sınıf metodlarının (statik metod), obje metodların, statik değişkenlere toplu değer vermek için kullandığımız statik bloğun veya statik olmayan değişkenlere toplu değer vermek için kullandığımız bloğun içerisinde tanımlanabilir. Yerel sınıfların genel gösterimi aşağıdaki gibidir ;

gösterim-12

public class  Sinif {

    public void metod() {
        public class YerelSinif {
            //...
        }     
    }
}

Yerel sınıflar, yanlızca içinde tanımlandıkları metodun veya bloğun içerisinde geçerlidir. Nasıl ki dahili üye sınıfıların çevreliyici sınıfları vardı, yerel sınıfların ise çevreliyici metodları veya blokları vardır. Yerel sınıflar tanımlandıkları bu metodların veya  blokların dışarısından erişilemezler.

Yerel sınıflara ait ilk özellikleri verelim ;

  • Yerel sınıflar tanımlandıkları metodun veya bloğun dışından erişilemezler.

  • Yerel sınıflar başka sınıflardan türetilebilir veya arayüzlere (interface) erişebilir.

  • Yerel sınıfların yapılandırıcıları olabilir.

Yukarıdaki özelikleri Java uygulamasında ispatlıyalım;

ör-Hesaplama6.java


interface Toplayici {
    public int hesaplamaYap() ;
}


public class Hesaplama6 {
	
         public int topla(int a, int b) {

             class Toplama6  implements Toplayici {
                private int deger1 ;
                private int deger2;
                public Toplama6(int deger1, int deger2) { // yapilandirici
                         this.deger1 = deger1;
                         this.deger2 = deger2;
                }
             
                public int hesaplamaYap() { // iptal etti (override)
                    return deger1+deger2;
                 }

             } // class Toplama6

             Toplama6  t6 = new Toplama6(a,b);
             return t6.hesaplamaYap();
         }

         public void ekranaBas() {
             // Toplama6  t6 = new Toplama6(2,6,);  // ! Hata !- Kapsama alaninin dI$I
         }

	public static void main(String args[]) {
		
	    Hesaplama6 h6 = new Hesaplama6();
            int sonuc = h6.topla(5,9);
            System.out.println("Sonuc  =  5 + 9  =  " + sonuc );
	}
} // class Hesaplama6

Bu örneğimizde yerel sınıf Toplama6 sınıfıdır. Yerel bir sınıf, başka bir sınıfdan türetilebilir veya bir arayüze (interface) erişip, onun gövdesiz (soyut) metodlarını iptal edebilir (override), aynı normal sınıflar gibi. Toplama6 yerel sınıfı, Hesapliyici arayüzüne eriştiğinden, bu arayüzün gövdesiz (soyut) metodu olan hesaplamaYap()  metodunu iptal etmiştir(override) .

Toplama6 yerel sınıfı, Hesaplama6 sınıfının topla() metodunun içerisinde tanımlanmıştır bunun anlamı, Toplama6 yerel sınıfına yanlızca topla() metodunun içerisinde erişilebileceğidir. Hesaplama6 sınıfının obje metodu olan (bu metoda ulaşmak için Hesaplama6 sınıfına ait obje oluşturmamız gerektiği anlamında..) ekranaBas() ın içerisinden, Toplama6 yerel sınıfına ulaşılamaz çünkü Toplama6 yerel sınıfı için ekranaBas() metodunun içerisi, kapsama alanının dışındadır. Uygulamamızın çıktısı aşağıdaki gibi olur ;

Sonuc = 5 + 9 = 14

Yerel sınıflara diğer özellikler aşağıdaki gibidir ;

  • Yerel sınıflar, içinde bulundukları metodun sadece final olan değişkenlerine ulaşabilirler.

  • Yerel sınıflar, statik veya statik olmayan metodların içerisinde tanımlanabilirler.

  • Yerel metodlar, private, protected ve public  erişim belirliyicisine sahip olamazlar sadece friendly erişim belirliyicisine sahip olabilirler.

  • Yerel metodlar, statik olarak tanımlanamaz.

Yukarıdaki özelikleri Java uygulamasında ispatlıyalım;


public class Hesaplama7 {

    public static int topla(int a, final int b) {
        int a_yedek = a ;
        class Toplama7 {
            private int x ; // dogru
            public int y ;  // dogru
            // protected int z = a_yedek ;  // ! Hata !
            int p ;  // dogru
            public int degerDondur() { 
                // int degera = a ;  // Hata
                int degerb = b ; 
                return b ;
            }

        } // class Toplama7

        Toplama7  t7 = new Toplama7();
        return t7.degerDondur();
    }

    public  void ekranaBas()  {
        /*  yerel metodlar sadece friendly erisim
            belirliyicisine sahip olabilirler 
         public class Toplama8 {
            public void  test() { 
            }
         }   // class Toplama8
        */
    }  // ekranaBas


    public static void main(String args[]) {

        int sonuc = Hesaplama7.topla(5,9);
        System.out.println("Sonuc   " + sonuc );
    }
} // class Hesaplama7

Toplama7 yerel sınıfı, Hesaplama7 sınıfının, statik olan  topla() metodunun içerisinde tanımlanmıştır ve sadece topla() metodunun içerisinde geçerlidir. Toplama7 yerel sınıfı, topla() metodunun içerisindeki final özelliğine sahip olan değişkenlere erişip onları kullanabilir. Bu sebebten dolayı, ilkel(primitive) int tipinde tanımlanmış olan a ve a_yedek değişkenlerine Toplama7 yerel sınıfının içerisinden erişelemez, bu bir hatadır.

Hesaplama7 sınıfının, obje metodu olan ekranaBas() içerisinde tanımlanmış olan Toplama8 yerel sınıfı hatalıdır. Hatanın sebebi Toplama8 yerel sınıfının public erişim belirliyicisine sahip olmasıdır.Yukarıda belirtildiği üzere, yerel sınıflar ancak friendly erişim belirliyicisine sahip olabilir. Uygulamamızın çıktısı aşağıdaki gibidir ;

Sonuc   9

İsimsiz  sınıflar (Anonymous classes)

İsimsiz sınıflar, isimsiz ifade edilebilen  sınıflardır. İsimsiz sınıflar havada oluşturulabildiklerinden dolayı bir çok işlem için çok avantajlıdır, özellikle event listeners (olay dinliyiciler) olaylarında (görsel programlada) sıkça kullanılırlar.İsimsiz sınıfların özellikleri aşağıdaki gibidir ; 

  • Diğer dahili sınıf çesitlerinde olduğu gibi, isimsiz sınıflar direk extends ve implements anahtar kelimelerini kullanarak, diğer sınıflardan türetilemez ve arayüzlere erişemez.

  • İsimsiz sınıfların herhangi bir ismi olmadığı için, yapılandırıcısıda (constructor) olamaz.

Yukarıdaki özelikleri Java uygulamasında ispatlıyalım;

ör-Hesaplama8.java 


interface Toplayici {
    public int hesaplamaYap() ;
}


public class Hesaplama8 {
	
         public Toplayici topla(final int a, final int b) {
             return new Toplayici() {
                     public int hesaplamaYap()  {
                        return a + b ; // final olan degiskenlere ulasabilir. 
                     }
             }; // noktali virgul sart

        } // topla, metod sonu
         

	public static void main(String args[]) {
		
	    Hesaplama8 h8 = new Hesaplama8();
            Toplayici t  = h8.topla(5,9);
            int sonuc = t.hesaplamaYap();
            System.out.println("Sonuc  =  5 + 9  =  " + sonuc );
	}
} // class Hesaplama8

Hesaplama8 sınıfının, topla() metodu Toplayici arayüzü tipinde bir obje geri döndürmektidir.Toplayici arayüzü tipinde bir obje geri döndürmek demek, Toplayici arayüzüne erişip onun gövdesiz (soyut) olan metodlarını iptal eden bir sınıf tipinde obje döndürmek demektir. Sonuçta bir arayüze (interface) ulaşan sınıf, ulaştığı arayüz (interface) tipinde olan bir değişkene bağlanabilirdi. " Buraya kadar tamam ama isimsiz sınıfımız nerede.. "  diyebilirsiniz. Hemen olaylara daha yakından bakalım.

gösterim-13

return new Toplayici() {
   public int hesaplamaYap()  {
      return a + b ; // final olan degiskenlere ulasabilir. 
   }
}; // noktali virgul sart

İşte isimsiz sınıfımız !! .Yukarıdaki ifade yerine, topla() metodun içerisinde yerel bir sınıf yazılabilirdi.

gösterim-14

public class BenimToplayicim implements Toplayici {
	public int hesaplamaYap()  {
            return a + b ; // final olan degiskenlere ulasabilir. 
        }
} // metod sonu
return new BenimToplayicim();

İsimsiz sınıfları, yerel sınıfların kısaltılmışı gibi düşünebilirsiniz. Yerel sınıflarda return new BenimToplayicim() yerine, isimsiz siniflarda hangi tipde obje döndüreleceği en başta belirtilir.

gösterim-15

return new Toplayici(); 

İsimsiz sınıflarda, yerel sınıflar gibi içinde bulundukları metodun sadece final olarak tanımlanmış değişkenlerine erişebilirler.

Yukarıdaki örneğimizde, isimsiz sınıfımız, Toplayici arayüzüne erişip onun gövdesiz (soyut) sınıflarını iptal etmiştir(override), buraya kadar herşey normal. Peki eğer isimsiz sınıfımız, yapılandırıcısı parametre olan bir sınıfdan türetilseydi nasıl olacaktı ?  Belirtildiği üzere isimsiz sınıfların yapılandırıcısı olamaz .

ör-Hesaplama9.java 


abstract  class  BuyukToplayici {
    private  int deger ;
    public BuyukToplayici(int x) {
            deger = x ;
    }
    public int degerDondur() {
        return deger ;
    }
    public abstract int hesaplamaYap() ; // iptal edilmesi gerek 
}


public class Hesaplama9 {
	
         public BuyukToplayici degerGoster( int gonderilen ) {
             return new BuyukToplayici( gonderilen ) {
                     public int hesaplamaYap()  { //iptal etme (override)
                             return  super.degerDondur() + 5  ;
                     }                         
             }; // noktali virgul sart

        } // degerGoster , metod sonu
         

	public static void main(String args[]) {
		
	  Hesaplama9 h9 = new Hesaplama9();
          BuyukToplayici bt  = h9.degerGoster(5);
          int sonuc =  bt.hesaplamaYap();
          System.out.println("Sonuc  =  " + sonuc );
	}
} // class Hesaplama9

BuyukToplayici sınıfı soyuttur (abstract) , bunun anlamı bu sınıfın içerisinde en az bir tane gövdesiz (soyut) metod olduğudur. BuyukToplayici sınıfının içerisindeki hesaplamaYap() gövdesiz (soyut) metoddur ve  bu  türetilen alt sınıflar tarafından iptal edilmek(override) zorundadır.

Bu örneğimizde ilginç olan iki nokta vardır. Birincisi isimsiz bir sınıfın, soyut bir metod dan türetilmesi, ikincisi ise türetilme yapılan BuyukToplayici metodunun yapılandırıcısının parametre alması. İsimsiz sınıfımızın yapılandırıcısı olamıyacağından dolayı, BuyukToplayici sınıfına ait parametre alan yapılandırıcıyı burada çağıramayız. Bu işlemi BuyukToplayici sınıfından türetilen isimsiz sınıfımızı oluştururken yapmalıyız.

İsimsiz sınıfların içerisinde, onları çevreleyen metodların final olmayan değişkenleri kullanılamaz. Peki ama bu örnekte kullanılıyor diyebilirsiniz.

gösterim-16

return new BuyukToplayici( gonderilen ) {
       public int hesaplamaYap()  {
                return  super.degerDondur() + 5  ;
       }                         
}; // noktali virgul sart

İlkel (primitive) int tipinde tanımlanmış gonderilen değişkeni, degerGoster() metoduna aittir, isimsiz sınıfımızın içerisinde kullanılmamıştır. return new BuyukToplayici( gonderilen ) ifadesi, degerGoster() metoduna dahil olduğundan bir sorun çıkmaz. Eğer uygulamamızı çalıştırırsak, çıktısı aşağıdaki gibi olur.

Sonuc = 10

Fiziksel İfade

İçerisinde Java kodları olan fiziksel bir dosya derlendiği (compile) zaman,  bu fiziksel dosya içerisinde tanımlanmış her bir sınıf için, fiziksel bir .class dosyası oluşturulur. Peki olaylar dahili sınıflar içinde aynımıdır ? Her bir dahili sınıf için, bir fiziksel .class dosyası oluşturulur mu ? Eğer oluşturuluyorsa ismi ne olur ? 

Her bir dahili sınıf için ( 3 çeşit dahili sınıf içinde gerçerli ) fiziksel .class dosyası oluşturulur. Bu .class dosyasının ismi ise, dahili sınıfı çevreleyen sınıfın ismi + $ + dahili sınıfın ismi şeklindedir. Hesaplama6.java örneğimizden bir gösterim yaparsak ;

gösterim-17

Hesaplama6$1$Toplama6.class
Hesaplama6.class

Hesaplama6 sınıfı, Toplama6 sınıfının çevreliyici sınıfıdır, böyle olunca  dahili sınıfımıza ait .class dosyasının ismi de , ÇevreliyiciSınıf$DahiliSınıf  formattında olduğunu görürüz. 

Java,  Hesaplama9.java örneğimizdeki isimsiz sınıf için nasıl bir .class dosyası oluşturur ? Cevabı hemen aşağıdadır ; 

gösterim-18

Hesaplama9$1.class
Hesaplama9.class

Java, Hesaplama9.java  içerisinde belirtilmiş isimsiz sınıfa ait .class dosyası oluştururken isim olarak 1'i (bir)   kullanmıştır. Mantıklı bir çözüm, sonuçta isimsiz sınıflarında, iş .class dosyası oluşturmaya geldiğinde bir isimleri olmaları gerekir.

Neden Dahili sınıflar  ? 

Dahili üye sınıflar, yerel sınıflar, isimsiz sınıflar hepsi çok güzel ama Sun MicroSystems neden bunlara ihtiyaç duymuş olabilir ? Şimdiye  kadar normal sınıflarımızla güzel güzel idare edebiliyorduk diyebilirsiniz. Dahili sınıfların var olmasındaki neden çoklu kalıtıma (Multiple inheritance) tam desteği sağlamaktır. 

Arayüzler (Interface)  ile çoklu kalıtım desteğini kısmen bulubiliyoduk ama bu tam değildi. Tam değildi çünkü bir sınıf iki normal sınıfdan türetilemiyordu, bunun sakıncılarını tartışmıştık. Fakat bazı zamanlarda, arayüzler dışında, normal sınıflara ihtiyaç duyabiliriz. Normal sınıflar derken, soyut (abstract) olmayan, problem çözmek için tasarlanmış işleyen sınıflardan bahsediyorum. Bu işleyen sınıfların iki tanesine aynı anda ulaşıp türetme yapımıyorduk ama  bu isteğimize artık dahili sınıflar ile ulaşabiliriz.

Java, dahili sınıflar ile çoklu kalıtım olan desteğini güvenli bir şekilde sağlamaktadır. Dahili sınıflar, kendilerini çevreleyen sınıfların hangi sınıfdan türetildiğine bakmaksızın bağımsız şekilde ayrı sınıflardan türetilebilir veya başka arayüzlere erişebilir.

Örnek Java kodumuzu incelemeden evvel, UML diyagramıa bir göz atalım.

Şekil-11

UML diyagramından olayları kuş bakışı görebiliyoruz. Olayları inceliyelim, AnaSinif dan türetilmiş TüretilmisSinif ın içerisinde iki adet dahili üye sınıf bulunmaktadır.Dahili üye sınıf olan BB ve CC sınıflarıda, B ve C sınıflarından türetilmişlerdir. Bu örneğimizdeki ana fikir, bir sınıfın içerisinde dahili üye sınıflar kullanılarak çoklu kalıtımın güvenli bir şekilde yapılabildiğini göstermektir. UML diyagramını Java uygulamasına dönüştürürsek ; 

ör-TuretilmisSinif.java


class AnaSinif {
    public void ekranaBas(String deger) {
        System.out.println( deger );
    }
}

class B {
    public String degerDondur() {
        return "B"; 
    }
}

class C {
    public int  topla(int a , int b) {
        return a+b ;
    }
}

public class TuretilmisSinif  extends AnaSinif {

    public class BB extends B {
        public BB() {  // yapilandirici
            ekranaBas( "Sonuc = " + degerDondur() );
        }

    }

    public class CC extends C { 
        public CC( int a , int b ) { // yapilandirici
            ekranaBas("Sonuc = " + topla(a,b) );
        }
    }

    public  static void main( String args[] ) {
           TuretilmisSinif.BB tbb  = new TuretilmisSinif().new BB();
           TuretilmisSinif.CC tcc  = new TuretilmisSinif().new CC( 6 , 9 );

    }

}

TuretilmisSinif sınıfımız, AnaSinif sınıfından türetilmiştir fakat bu dahili sınıfların başka sınıflardan türetilmelerine engel teşkil etmez. Her bir dahili sınıfın kendine ayit bir durumu olabilir. Dahili sınıflar kendilerini çevreleyen sınıflardan bağımsızdır. Dahili sınıflar ile onları çevreleyen sınıflar arasında kalıtımsal bir ilişki olmak zorunda değldir, geçen bölümlerde incelediğimiz "bir" ilişkisi, Elma bir Meyvadır gibi.

Örneğimize geri dönersek, B sınıfından türetilmiş BB sınıfı ve C sınıfından türetilmiş CC sınıfı, Anasinif sınıfına ait ekranaBas() metodunu kullanarak sonuçlarını ekrana yansıtabilmektedirler. Olaylara kuş bakışı bakarsak, bir sınıfın sanki üç ayrı işleyen (normal) sınıftan  güvenli ve kolay bir şekilde türetilmiş gibi olduğunu görürürüz.

Uygulamamızın çıktısı aşağıdaki gibi olur ; 

Sonuc = B
Sonuc = 15

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

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

 

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

© Copyright  2002