Paket Yapıları

Javada, paketler, bir grup alakalı veya benzer sınıfları gruplamak için kullanılan yapılardır. Bunları tıpkı dizinler (klasör, folder) gibi düşünebilirsiniz. Aslında dizinlerden öte bir kabiliyete sahiptirler. Daha evvel erişim düzenleyiciler başlığında bahsettiğimiz bu kabiliyeti, bu yazımızda detaylandıracağız. Paketler, ayrıca, benzer isimli ancak farklı görevleri yürüten elemanların da birbirinden ayrılmasını sağlar. Bu noktada paketlerin temel fonksiyonunun, çok genel bir tanımla, düzeni sağlamak olduğunu düşünebiliriz.

Javada 2 tür paket yapısı bulunmaktadır.

  • Built-in paketler: Javada hali hazırda tanımlanmış paketlerdir.
  • Kullanıcı tanımlı paketler: Kullanıcı/geliştirici tarafından oluşturulmuş paketlerdir.

Bu iki paket türünü daha yakından inceleyeceğiz. Bu da 9. projemizi yaratacağımız anlamına geliyor. Proje ismimiz 09 – Packages olacak.

Öncelikle bir paketin kullanılmasını nasıl sağlayacağımızı inceleyelim. Bunun için karşımıza yeni bir anahtar sözcük çıkacak. Bu sözcük import sözcüğüdür. Bir başka paketi veya sınıfı istediğimiz sınıf içerisinden kullanabilmemiz için import sözcüğüne ihtiyaç duymaktayız. Öncelikle bu sözcüğe sözdizim olarak göz atalım.

import paket.ismi.Class; // Bir sınıfın import edilmesi
import paket.ismi.*;     // Bir paketin tamamen import edilmesi

Bir sınıf içerisinden başka bir sınıfı veya paketi kullanmak istersek sınıfımızın tepesine yukarıdaki tanımlamayı yapmaya ihtiyaç duyarız. Bu durumu test edebilmek için gelin öncelikle bir paket tanımı yapalım.Bunun için projemizde src dizinine veya default package üzerine sağ tıklayıp New>Package yolunu takip ediyoruz.

Yeni bir paket yaratmak

Ardından açılan pencerede paketimize isim veriyoruz. Bu örnek için paketimize dersler ismini vereceğim.

Genel bir uygulama olarak paket isimleri küçük harflerle yazılır

Bu paketin içine 2 adet sınıf kodlayacağız. Bunlar sırasıyla Matematik ve Edebiyat sınıfları olsun. İçlerine de dersBilgisi() isminde bir method koyup geri dönüşünü String olarak belirteceğim. Yeni paketin üstüne sağ tıklayıp sınıfları oluşturabilirsiniz.

package dersler;

public class Matematik {

	public String dersBilgisi() {
		return "Matematik dersi";
	}
	
}
package dersler;

public class Edebiyat {

	public String dersBilgisi() {
		return "Edebiyat dersi";
	}
	
}

Bu sınıflarda dikkatinizi çekmiştir ki, paket tanımları editör tarafından otomatik olarak sınıfın tepesine “package dersler;” ibaresi ile eklendi. Şimdi TestClass sınıfımıza gidelim ve bu sınıflardan birer instance yaratalım.

import dersler.Edebiyat;
import dersler.Matematik;

public class TestClass {

	public static void main(String[] args) {
		
		Matematik matematikDersi = new Matematik();
		System.out.println(matematikDersi.dersBilgisi());
		
		Edebiyat edebiyatDersi = new Edebiyat();
		System.out.println(edebiyatDersi.dersBilgisi());
		
	}
}

Bu ifadeleri yazdığımızda editör otomatik olarak sınıfın tepesine import ifadelerini yerleştirir. Sözdizimine baktığımızda iki sınıfın ayrı ayrı import edildiğini görürüz. Bunun yerine import satırlarını tek bir ifadeye de indirebilirdik. Yani aşağıdaki kod ile aynı sonuca ulaşırız.

import dersler.*;  //Bu kısma dikkat edin

public class TestClass {

	public static void main(String[] args) {
		
		Matematik matematikDersi = new Matematik();
		System.out.println(matematikDersi.dersBilgisi());
		
		Edebiyat edebiyatDersi = new Edebiyat();
		System.out.println(edebiyatDersi.dersBilgisi());
		
	}
}

O halde işleri biraz daha karmaşıklaştıralım. Yeni bir paket daha yaratalım ve bu paketin ismini bolumler olarak belirleyelim. Bunları üniversitenin bölümleri gibi düşüneceğiz ve özellikle bir çakışma yaratmak için bu paketin altına yine Matematik ve Edebiyat sınıflarını kodlayacağız. Hatta ve hatta yine dersBilgisi() methodunu bu sınıfların içine koyacağız. Ancak anlamlı olması açısından bu methodun dönüş değerlerini biraz daha farklıklaştıralım.

package bolumler;

public class Matematik {

	public String dersBilgisi() {
		return "Matematik bolumu dersleri";
	}
	
}
package bolumler;

public class Edebiyat {

	public String dersBilgisi() {
		return "Edebiyat bolumu dersleri";
	}
	
}

Peki herhangi bir değişiklik yapmadığımız TestClass bu durumda hangi sınıfları kullanacak? Tahmin edildiği üzere dersler paketindeki sınıfların çıktılarına ulaşırız. Ancak aynı anda bolumler paketini de import edersek editörümüz bizi uyaracaktır. Çünkü import edilen paketlerin içinde aynı isimde sınıflar bulunmaktadır. Bu da bir çakışma anlamına gelmektedir. Derleyiciye hangi paketin kullanılması gerektiğini söylemeye ihtiyaç duyarız.

Paket çakışmasına (conflict) bir örnek

Kodu bu hatalı haliyle çalıştırdığımızda derleyiciden konuyla ilgili uyarıyı alırız. Nihayetinde programımız derleme hatası sebebiyle çalışmaz.

Çıktı:
Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
     The type Matematik is ambiguous
     The type Matematik is ambiguous
     The type Edebiyat is ambiguous
     The type Edebiyat is ambiguous
 at TestClass.main(TestClass.java:8)

Sınıfımızdaki import ifadelerinden dersler ile ilgili olanı silersek görürüz ki, çıktıda bolumler paketinin sınıflarının cevaplarını alırız. Ancak eğer ihtiyacımız bolumler paketinden Matematik sınıfı ve dersler paketinden Edebiyat sınıfını kullanmaksa, bu sınıfların doğrudan import edilmesi ile çakışmanın önüne geçmiş oluruz.

İki paketi bir arada kullanmak istiyorsak, yani hem bolumler hem de dersler paketinden Matematik veya Edebiyat sınıflarını kullanmak isteseydik ,sınıf içerisindeki değişken tanımlamalarında, tam paket adını da kullanmamız gerekir. Bunun örneğini aşağıdaki gibi görebiliriz.

public class TestClass {

	public static void main(String[] args) {
		
		dersler.Matematik matematikDersi = new dersler.Matematik();
		System.out.println(matematikDersi.dersBilgisi());
		
		dersler.Edebiyat edebiyatDersi = new dersler.Edebiyat();
		System.out.println(edebiyatDersi.dersBilgisi());
		
		bolumler.Matematik matematikBolumu = new bolumler.Matematik();
		System.out.println(matematikBolumu.dersBilgisi());
		
		bolumler.Edebiyat edebiyatBolumu = new bolumler.Edebiyat();
		System.out.println(edebiyatBolumu.dersBilgisi());
		
	}
}
Çıktı:
Matematik dersi
Edebiyat dersi
Matematik bolumu dersleri
Edebiyat bolumu dersleri

Dikkat edileceği üzere bu yöntemde kod daha kirli gözükse de, import ifadesi ortadan kalkmıştır. Bu da bir sınıfı kullanmanın import sözcüğü olmadan da mümkün olduğunun göstergesidir. Aynı zamanda buraya kadar çıkaracağımız başka bir ders ise, aynı isimde sınıflara ihtiyaç duyuyorsak bunları kategorize etmenin en mantıklı yolu uygun şekilde paketlere bölmektir.

Built-in Paketler

Built-in (Dahili, yerleşik) paketler java tarafından bize hazır olarak sunulan paketlerdir. TestClass sınıfımızın üzerinden bunu yakından inceleyelim. Javada tarihlerle ilgili olarak hazır şekilde tanımlanmış bir Date sınıfı vardır. bu sınıf java.util paketinin altında bulunmaktadır. Date sınıfını kullanmak istersek ya bu paketi import ederiz ya da Date sınıfının yerini değişken tanımlama esnasında belirtiriz. Bu ikinci yöntem az önce import ifadesi kullanmadan yaptığımız tanımlamalar ile tamamen aynıdır.

private static void builtIn() {

	Date d = new Date();

	System.out.println(d.toString());

}

Yukarıdaki methodu yazdığımızda göreceğiz ki sınıfımızın tepesinde bir import ifadesi yer alacaktır.

import java.util.Date;

Bu noktada editör kullanımı ile ilgili bir tüyo vereceğim. Javada built-in olarak gelen birden fazla Date sınıfı vardır. Hem doğru sınıfı import edebilmek, hem de paketin tam yolunu bilmiyorsanız yardım almak için ctrl+boşluk kısayolunu kullanabilirsiniz.

ctrl+boşluk kısayolunun kullanımı

Bu kısayolu import seviyesinde kullanarak javadaki diğer built-in paketlere göz atabilirsiniz. Ayrıca bu kısayol built-in olmayan paketlerde de işe yaramaktadır.

Kullanıcı Tanımlı Paketler

Bu yazıda kullandığımız dersler ve bolumler paketleri kullanıcı tarafından tanımlanmış paketlere örnektir. Projenizin disk üzerinde saklandığı dizine giderseniz, bu paketleri birer dizin olarak görürsünüz.

Kullanıcı tanımlı paketlerin disk üzerinde saklanması

Pekala bu bize bir fikir verebilir. Bazen dosyalarımızı düzenlerken iç içe dizinler oluştururuz. Peki neden iç içe dizinler mantığını, paketler için kullanmayalım ki? Bu gayet mümkündür. Hatta bu tarz paketlere alt paketler (subpacakage) ismi verilmektedir. Doğrudan bir örnek ile bu konuyu inceleme altına alalım.

Öncelikle aircraft (hava taşıtı) isminde bir paket oluşturalım. Ardından bu pakete sağ tıklayıp glider (planör) ve zeppelin (zeplin) isminde alt paketler oluşturacağız. Ancak bu iki alt paket için, paket oluşturma ekranımızda minik bir detay gözümüze çarpacak. Alt paket oluştururken üst paketin ismini de yazmak durumundayız. Ekran görüntülerinde bunu görebilirsiniz.

Bu paketlerin içerisine 5 adet sınıf kodlayacağız. Bunlardan ilk ikisi aircraft paketinin altına Aircraft ve AircraftUtility sınıfları, üçüncüsü aircraft.glider paketinin altına Glider sınıfı, dördüncü olarak aircraft.glider paketine GliderUtility sınıfı ve son olarak aircraft.zeppelin paketinin altına Zeppelin sınıfı olacak.

Aircraft sınıfının yaratılması. Package alanına dikkat ediniz.

Şimdi de bu sınıfların içini dolduracağız. Bu sınıfları belirli bir mantık silsilesi içerisinde dolduracağım.

package aircraft;

public class AircraftUtility {
	
	public void runEngine() {
		System.out.println("AircraftUtility.runEngine()");
	}
	
	public void stopEngine() {
		System.out.println("AircraftUtility.stopEngine()");
	}
	
	protected void fixAircraft() {
		System.out.println("AircraftUtility.fixAircraft()");
	}

}
package aircraft;

public class Aircraft {
	
	void fixAircraft() {
		AircraftUtility au = new AircraftUtility();
		au.fixAircraftWithUtility();
	}

}
package aircraft.glider;

import aircraft.AircraftUtility;

public class Glider {

	public void moveGlider() {
		AircraftUtility au = new AircraftUtility();
		au.runEngine();
		
		GliderUtility gu = new GliderUtility();
		gu.flyGlider();
	}

	public void stopGlider() {
		AircraftUtility au = new AircraftUtility();
		au.stopEngine();
	}
	
}
package aircraft.glider;

import aircraft.AircraftUtility;

class GliderUtility extends AircraftUtility {

	public void flyGlider() {
		this.fixAircraftWithUtility();
		System.out.println("GliderUtil.flyGlider()");
	}
}
package aircraft.zeppelin;

import aircraft.AircraftUtility;

public class Zeppelin {
	
	public void moveZeppelin() {
		AircraftUtility au = new AircraftUtility();
		au.runEngine();
	}

	public void stopZeppelin() {
		AircraftUtility au = new AircraftUtility();
		au.stopEngine();
	}
	
}

Burada dikkatinizi çekmek istediğim nokta erişim düzenleyicilerin kullanımıdır. AircraftUtility sınıfındaki fixAircraftWithUtility() methodu protected olarak tanımlanmıştır. Aircraft sınıfındaki fixAircraft() methodu ise boş erişim düzenleyici ile tanımlanmıştır. GliderUtility sınıfının kendisi ise boş erişim düzenleyici ile (package private) tanımlanmıştır. Bu tasarım ile şunları söyleyebiliriz;

  • AircraftUtility.fixAircraftWithUtility() methodu sadece aircraft paketindeki sınıflar tarafından ulaşılabilir durumdadır. Alt paketler ve diğer paketler bu methodu doğrudan kullanamaz. Ancak bu sınıfı extend eden GliderUtility sınıfı, farklı pakette olmasına rağmen bu methodu kullanabilir durumdadır.
  • Aircraft.fixAircraft() methodu da benzer bir durumdadır. Ancak erişim düzenleyicimiz protected yerine boş olduğundan extend eden sınıflar bu methodu kullanamaz. Bir sınıfı Aircraft sınıfından extend ederek bunu deneyimleyebilirsiniz.
  • GliderUtility sınıfı (methodları değil kendisi) sadece aircraft.glider paketi içerisinden kullanılabilir.

Bu yazıdan anlayabileceğiniz üzere paketler düzen sağlamanın ötesinde bir kabiliyete sahiptir. Paketler düzen sağlamanın yanında kodlarımızı tekrar kullanabilme, isim karışıklıklarının önlenmesi ve erişim düzenlemenin ana direklerindendir.

Yazımızı burada noktalıyoruz. Son olarak yazımızın ilk kısmı için yaptığımız örnekleri toparladığım TestClass sınıfımızı paylaşıyorum. Bir sonraki yazımızda görüşmek üzere.

import java.util.Date;

public class TestClass {

	public static void main(String[] args) {

		userDefined();

		builtIn();
	}

	private static void userDefined() {

		dersler.Matematik matematikDersi = new dersler.Matematik();
		System.out.println(matematikDersi.dersBilgisi());

		dersler.Edebiyat edebiyatDersi = new dersler.Edebiyat();
		System.out.println(edebiyatDersi.dersBilgisi());

		bolumler.Matematik matematikBolumu = new bolumler.Matematik();
		System.out.println(matematikBolumu.dersBilgisi());

		bolumler.Edebiyat edebiyatBolumu = new bolumler.Edebiyat();
		System.out.println(edebiyatBolumu.dersBilgisi());

	}

	private static void builtIn() {

		Date d = new Date();

		System.out.println(d.toString());

	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *