Arayüzlerde Varsayılan Metotlar (Default Methods) – I

Java SE 8, son zamanlardaki en fazla değişiklik içeren ve bundan dolayı da heyecan getiren Java SE sürümü oldu. Çünkü Java SE 8 ile gelen değişiklikler, abartmayayım ama, bazı konulardaki düşüncemizi kökten değiştiren cinsten.

Tabi dillerde değişiklik yapmak, hele temel mekanizmalarıyla oynamak kolay değil. Örneğin switch ifadelerinde String kullanımı için senelerce beklemiştik. Benzer şey Java’ya fonksiyonel özelliklerin eklenmesinde de oldu. Java SE 8’i, Java SE 1.2 ve 5.0 gibi, diğerleri arasında öne çıkan sürümlerden olması, Java’ya, çehresini değiştirecek fonksiyonel yapılar katmasıyla ilgili. Ve muhtemelen en çarpıcı olan değişiklik ise arayüzlerin (interfaces) artık gerçekleştirmesi olan metotlara sahip olması.

Malum, arayüz, kavramsal olarak da Java’da “interface” anahtar kelimesiyle ifade edilen yapı olarak da sadece şekil sağlayan, bir davranışın “neliği”ni ifade eden, “nasıllığı”nı, onu gerçekleştiren sınıflara bırakan bir yapıdır. Bu anlamda arayüze, İngilizce’de “contract” de denir. “program to an interface, not an implementation” ya da “design by contract” gibi felsefe ve yaklaşımlar hep arayüz kavram ve mekanizmalarının ifade ettiği soyutluk üzerine bina edilmişlerdir.

Durum böyle iken Java SE 8’de, arayüzlerin bir gerçekleştirmeye yani koda sahip metotları içermesi mümkün hale geldi. Artık Java’da bir arayüz sadece soyut (abstract) metotlara değil,  default (varsayılan) ve statik metotlara da sahip olabilir.

Default metotlar metot arayüzünde “default” anahtar kelimesi ile tanımlanır ve arayüzdeki soyut metotların aksine bir gerçekleştirmeye sahip olmaları zorunludur.

public interface Printable {
	
	void view();
	
	void print();
	
	default void format(){
		System.out.println("Printable is being formatted.");
	}
}

 

Bu durumda Printable arayüzünden miras alan sınıfların default olan format() metoduna bir gerçekleştirme vermeleri gerekmeyecektir. Çünkü bu metodun Printable arayüzünden devir alınan hali zaten bir gerçekleştirmeye sahiptir.

public class Document implements Printable {

	@Override
	public void view() {
		System.out.println("Document is being viewed.");
	}

	@Override
	public void print() {
		System.out.println("Document is being printed.");
	}
}

 

Yukarıdaki kodun, yazının basında bahsettiğim arayüz kavramına alışkın bir zihinde karışıklığa neden olacağı kesin gibidir. Anlamayı kolaylaştırmak için neden böyle bir şeye ihtiyaç duyulduğunu hızlıca açıklayalım.

Eğer projenizde kullandığınız bir arayüze yeni bir metot ekleyip, onu derledikten sonra .class dosyasını projenizdeki yerine koyarsanız, tekrar derlemediğiniz müddetçe, o arayüzü kullanan, örneğin gerçekleştiren (implement eden), sınıflar çalışmaya devam ederler. Buna “binary compatibility” (ikili uyumluluk) denir. Ama o arayüzü kullanan bir sınıfı tekrar derlemeye kalkarsanız, örneğin bir hatasını düzelttikten sonra, derleme hatası alırsınız çünkü bu durumda source compatibility (kaynak uyumluluğu) yoktur.

Arayüzlerde default olarak tanımlanan ve bir gerçekleştirmeye sahip olan metotlar, arayüzlerin eski sürümlerini kullanan sınıflar ile arayüzün default metotlarla zenginleştirilmiş yeni sürümü arasında kaynak uyumluluğu sağlamak amacına hizmet etmektedir. Diğer bir deyişle, bir arayüze yeni bir metot eklediğinizde, o arayüzden miras devir alan sınıfların tekrar derlenmeleri halinde derleme hatası vermemeleri için yapmaları gereken, arayüzdeki yeni metotlara bir gerçekleştirme sağlama zorunluluğundan kurtulmanın yolu, arayüze eklenen metotları default olarak tanımlamak ve bir gerçekleştirme vermektir.

Statik metotlar ise, bildiğimiz, sınıflarda tanımlanan statik metotlarla aynı yapıdadırlar. Arayüzlerdeki statik metotların tek farkı devir alınmayıp, sadece ve sadece tanımlandığı arayüz üzerinden çağrılabilmesi kısıtıdır. Örnekle açıklayalım:

package org.javaturk.oofp.ch04.staticMethods;

public interface Printable {
	
	void view();
	
	void print();
	
	default void format(){
		System.out.println("Printable is being formatted.");
	}
	
	static void startPrinting(){
		System.out.println("Printing has been started.");
	}
}

 

package org.javaturk.oofp.ch04.staticMethods;

public class Document implements Printable {

	@Override
	public void view() {
		System.out.println("Document is being viewed.");
	}

	@Override
	public void print() {
		System.out.println("Document is being printed.");
	}
}

Bu durumda test sınıfımız şöyle olacaktır:

package org.javaturk.oofp.ch04.staticMethods;

public class TestDefaultMethods {

	public static void main(String[] args) {
		Printable printable = new Document();
		printable.print();
		printable.format();
		
		Printable.startPrinting();
		
//		Document.startPrinting();  // Can't do this!
	}
}

Yukarıdaki örnekte “Document.startPrinting();” satırının problemli olduğuna dikkatinizi çekmek isterim.

Arayüzlerde tanımlanan tüm metotlar, soyut, default ya da statik olsun, her zaman public erişim belirtecine varsayılan halde sahiptirler.

Bir sonraki yazıda kafanızı hepten karıştırmak üzere 🙂

Toplam görüntülenme sayısı: 975