Instanceof Sözcüğü

Instanceof sözcüğü javada iki tipi karşılaştırıp geriye true veya false değeri dönen bir anahtar sözcüktür. Konsept olarak bu operatör is-a ile oluşturulmuş soru cümlesinin cevabını verir. Solundaki nesnenin sağındaki yapının bir örneği olup olmadığını döner. (Is {sol taraf} a {sağ taraf}? – {sol taraf} bir {sağ taraf} mıdır?) 15 – Instanceof isimli projemiz içerisinde bu sözcüğün kullanımı inceleyeceğiz.

Bu projemizde ele alacağımız konu bir saat satıcısı olacak. Saat satan bir firmanın farklı modellerde ve çalışma şekillerindeki saatlerini, web siteleri üzerinden satmak istediğini düşünelim. Bunu yaparken de saat tipleri için bir interface tanımlanmış olsunlar. Bu arayüzün implementasyonları olan sınıflar ise kendi içinde alt sınıflara sahip olsun. Öncelikle buna uygun bir interface yazalım. Bir saatin temel görevi zamanı göstermektir. Dolayısı ile interface bunu barındırmalı düşüncesiyle hareket edeceğim.

public interface Watch {

	public String getTime();
}

Şimdi ise bu interface üzerinden iki ayrı implementasyon oluşturalım. Bu sınıflardan birisi analog saatleri diğeri ise dijital saatleri temsil etsin.

public class AnalogWatch implements Watch {

	@Override
	public String getTime() {
		return "AnalogWatch.getTime()";
	}

}

public class DigitalWatch implements Watch {

	@Override
	public String getTime() {
		return "DigitalWatch.getTime()";
	}

}

Elimizdekilerle birlikte bir de test sınıfımızı oluşturalım ve instanceof sözcüğünün kullanımına bakalım.

public class TestClass {
	
	public static void main(String[] args) {
		
		AnalogWatch aw = new AnalogWatch();
		DigitalWatch dw = new DigitalWatch();
		Watch w = new AnalogWatch();
		
		if (aw instanceof Watch) {
			System.out.println("aw is-a Watch");
		} else {
			System.out.println("aw is-not-a Watch");
		}
		
		if (dw instanceof DigitalWatch) {
			System.out.println("dw is-a DigitalWatch");
		} else {
			System.out.println("dw is-not-a DigitalWatch");
		}
		
		if (w instanceof DigitalWatch) {
			System.out.println("w is-a DigitalWatch");
		} else {
			System.out.println("w is-not-a DigitalWatch");
		}
		
	}
}
Çıktı:
aw is-a Watch
dw is-a DigitalWatch
w is-not-a DigitalWatch

Bu kodu parça parça inceleme altına alalım.

  • Bu örnekte 3 ayrı obje görülmektedir. aw objesi AnalogWatch tipinde olup AnalogWatch() constructor ile yaratılmıştır. dw objesi DigitalWatch tipinde olup DigitalWatch() constructor ile yaratılmıştır. Son olarak w objesi ise Watch tipinde olup AnalogWatch() constructor ile yaratılmıştır.
  • Bu koddaki ilk if ifadesi aw değikeninin, Watch tipiyle temsil edilip edilemeyeceğini kontrol etmektedir. Bu ifadenin cevabı doğrudur. Bu ifadeleri yukarıdan aşağı (hiyerarşik) gelen bir aktarım gibi düşünmeliyiz. Bu durumda hem AnalogWatch hem de DigitalWatch sınıfından yaratılan nesneler, aynı zamanda bir Watch instance olarak kabul edilir.
  • İkinci ifade ise doğrudan true cevap dönecek bir ifadedir. Zira hem sol hem sağdaki ifade aynı yeri işaret eder. Burada instanceof ifadesinin sağında AnalogWatch sınıfı olsaydı kod compile olmazdı. Çünkü bu AnalogWatch ve DigitalWatch arasında dikey (hiyerarşik) bir miras ilişkisi yoktur.
  • Son koşulumuzda ise w ve DigitalWatch karşılaştırılmaktadır. Burada derleme hatası alınmaz. Çünkü bu objenin tanımlama anındaki tipi ve DigitalWatch arasında hiyerarşik bir ilişki mevcuttur. Lakin w değişkeni AnalogWatch sınıfı üzerinden oluştuğu için bu ifade false dönüş yapar.

Bu kod parçasından anlaşılacağı gibi instanceof anahtar sözcüğü, bir nesnenin hem bir interface implementasyonu olup olmadığını, hem de bir sınıfa ait instance olup olmadığını denetleyebilmektedir. O halde elimizdeki sınıfları biraz daha genişletelim. Saat firmasının sattığı dijital saatlerin 2 ayrı modeli olduğunu varsayalım. Bunları DigiModelA ve DigiModelB sınıfları temsil etsin.

public class DigiModelA extends DigitalWatch {

}


public class DigiModelB extends DigitalWatch {

}

Böylece hiyerarşiyi genişletmiş olduk. Şematize etmek istersek aşağıdaki gibi bir zincirimiz oluşmuş durumdadır.

  • Watch
    • DigitalWatch
      1. DigiModelA
      2. DigiModelB

Bu zincirdeki her bir alt eleman, zincirde üstte kalan yapının örneğidir. Bunu aşağıdaki kod ile gözlemleyelim.

DigiModelA da = new DigiModelA();
DigiModelB db = new DigiModelB();

if (da instanceof Watch) {
	System.out.println("da is-a Watch");
} else {
	System.out.println("da is-not-a Watch");
}

if (db instanceof DigitalWatch) {
	System.out.println("db is-a DigitalWatch");
} else {
	System.out.println("db is-not-a DigitalWatch");
}
Çıktı:
da is-a Watch
db is-a DigitalWatch

Bu kod parçasında bir da-db karşılaştırılması yapılsaydı derleme hatası alırdık. Sebebi yine bu nesnelerin sınıfları arasındaki bağlantının hiyerarşik bir hat üzerinde olmamasıdır.

Şu ana kadar incelediğimiz kısım instanceof sözcüğünün nasıl bir kullanım alanı olabileceğine dair bir fikir oluşturmamış olabilir. Bunun için yaklaşımızı biraz tersine çevirelim. Varsayalım ki firma saatlerini web sitesinde listeliyor ve siz de beğendiğiniz bir saatin üstüne tıklayıp detay sayfasına gidiyorsunuz. Firma satışlarda faydalı olacağını düşündüğü için analog saatlerinin üretildiği ülkeyi ekranlarında göstermek istiyor. Ancak olur da aynı taktiği dijital saatlerde de kullanırız diyerek, esnek bir sistem yapılmasını istiyor. IT departmanı ise bunun için bir utility sınıfı hazırlayıp gerektiğinde kodu değiştirmeyi tercih ediyor. Şimdi bu sınıfın varsayımsal olarak neye benzediğine bakalım.

public class WatchUtility {

	public static String getProducerCountry(Watch watch) {
		
		if (watch instanceof AnalogWatch) {
			return "WatchUtility.getProducerCountry()";
		}
		
		return "";
	}
}

Bu sınıf içerisindeki methoda baktığımızda görürüz ki denetlenmek istenen nesnenin tipi (AnalogWatch) methoda parametre olarak geçirilen değişkene göre (Watch) daha özelleşmiştir. Bu özelleştirmeyi nasıl kullanabileceğimizi göstermek adına örnek bir kod yazalım.

DigiModelA da = new DigiModelA();
DigiModelB db = new DigiModelB();
DigitalWatch dw = new DigitalWatch();
AnalogWatch aw = new AnalogWatch();
Watch w1 = new AnalogWatch();
Watch w2 = new DigitalWatch();
Watch w3 = new DigiModelA();

System.out.println(WatchUtility.getProducerCountry(da));
System.out.println(WatchUtility.getProducerCountry(db));
System.out.println(WatchUtility.getProducerCountry(dw));
System.out.println(WatchUtility.getProducerCountry(aw));
System.out.println(WatchUtility.getProducerCountry(w1));
System.out.println(WatchUtility.getProducerCountry(w2));
System.out.println(WatchUtility.getProducerCountry(w3));
Çıktı:
No result
No result
No result
WatchUtility.getProducerCountry()
WatchUtility.getProducerCountry()
No result
No result

Burada çok biçimliliğin güzel bir örneğini görmekteyiz. Değişken tanımlarında sol tarafta kalan ifadelerin her daim hiyerarşik yapıda denk veya yukarıda kalan yapılar olması gerektiğini görüyoruz. Ancak denetleme esnasında seviyenin ne olduğunu instanceof sözcüğü ile çözümleyebiliyoruz. Methodumuz ise en tepedeki yapıyı input olarak kabul ettiğinden, farklı farklı bir çok değişkeni kullanabiliyoruz.

DigitalWatch dw = new DigitalWatch();

if (dw instanceof DigiModelA) {
	System.out.println("dw is-a DigiModelA");
} else {
	System.out.println("dw is-not-a DigiModelA");
}
Çıktı:
dw is-not-a DigiModelA

Burada da benzer bir çözümleme görmekteyiz. DigitalWatch ve DigiModelA sınıfları hiyerarşik olarak ilişkili olduğundan derleme hatası almadan instanceof anahtar sözcüğünü kullanabilmekteyiz. Ancak çıktıdaki gibi dw bir DigiModelA örneği olmadığından ifade false döner ve kod else bloğuna düşer.

Utility sınıfımızın içerisine tekrar odaklanalım. Biz methoda gelen parametreye göre bir işlem yaptık. Burada küçük bir farklılık yaratarak AnalogWatch sınıfımıza yeni bir method ekleyelim. Bu methodumuzla, analog bir saatin detayları incelendiğinde üreticisini bilgilendireceğimizi varsayalım. methodumuzun ismi inform() olsun ve geriye değer dönmesin.

public class AnalogWatch implements Watch {

	@Override
	public String getTime() {
		return "AnalogWatch.getTime()";
	}
	
	public void inform() {
		System.out.println("AnalogWatch.inform()");
	}
}

Şimdi utility sınıfımıza geri dönelim. Az evvel eklediğimiz methodda bir de bu inform methodunu çağıralım. Bu durumda hata alırız. Zira methoda geçen parametre Watch arayüzü ile temsil edilmektedir. Bu interface içerisinde ise inform isminde bir method yoktur.

Ancak sınıfın içerisine gelen parametrenin aslında AnalogWatch tipinde olduğunu biliyoruz. Dolayısı ile içinde çalıştırabileceğimiz bir inform methodu olmalı. O halde bunu derleyiciye nasıl bildrimemiz gerektiğine odaklanmamız gerekiyor. Bu bir dönüşüm işlemidir ve javada bunu yapabilmek mümkündür. Bu işleme casting veya class casting ismi verilmektedir. Değişkenin soluna bir parantez açıp, parantez içerisine dönüştürmek istediğimiz tipi yazarak bu işlemi yapabiliriz.

public class WatchUtility {

	public static String getProducerCountry(Watch watch) {
		
		if (watch instanceof AnalogWatch) {
			AnalogWatch aw = (AnalogWatch) watch;
			aw.inform();
			// ((AnalogWatch) watch).inform(); //alternatif yöntem
			return "WatchUtility.getProducerCountry()";
		}
		
		return "No result";
	}
}

Casting işlemi sırasında parantez içerisine hatalı bir ifade (Yanlış sınıf) uygulama çalışırken (Runtime) hata alırsınız. Dolayısı ile bu işlemi dikkatli şekilde yapmanız gerekir. Zira derleyici sizi bu konuda uyarmamaktadır.

Test edebilmek adına az önceki 7 değişkenli kodumuzu tekrar çalıştırıp çıktısına bakalım.

Çıktı:
No result
No result
No result
AnalogWatch.inform()
WatchUtility.getProducerCountry()
AnalogWatch.inform()
WatchUtility.getProducerCountry()
No result
No result

Görüldüğü üzere AnalogWatch sınıfımızın örneği olan değişkenlerde inform methodu da çalışmış oldu.

Son olarak tüm yazıdaki örnekleri bir araya topladığım TestClass sınıfımızı da paylaşarak yazıyı bitiriyorum.

public class TestClass {

	public static void main(String[] args) {

		case1();
		case2();
		case3();
		case4();
	}

	private static void case1() {

		AnalogWatch aw = new AnalogWatch();
		DigitalWatch dw = new DigitalWatch();
		Watch w = new AnalogWatch();

		if (aw instanceof Watch) {
			System.out.println("aw is-a Watch");
		} else {
			System.out.println("aw is-not-a Watch");
		}

		if (dw instanceof DigitalWatch) {
			System.out.println("dw is-a DigitalWatch");
		} else {
			System.out.println("dw is-not-a DigitalWatch");
		}

		if (w instanceof DigitalWatch) {
			System.out.println("w is-a DigitalWatch");
		} else {
			System.out.println("w is-not-a DigitalWatch");
		}

	}

	private static void case2() {

		DigiModelA da = new DigiModelA();
		DigiModelB db = new DigiModelB();

		if (da instanceof Watch) {
			System.out.println("da is-a Watch");
		} else {
			System.out.println("da is-not-a Watch");
		}

		if (db instanceof DigitalWatch) {
			System.out.println("db is-a DigitalWatch");
		} else {
			System.out.println("db is-not-a DigitalWatch");
		}

	}

	private static void case3() {

		DigiModelA da = new DigiModelA();
		DigiModelB db = new DigiModelB();
		DigitalWatch dw = new DigitalWatch();
		AnalogWatch aw = new AnalogWatch();
		Watch w1 = new AnalogWatch();
		Watch w2 = new DigitalWatch();
		Watch w3 = new DigiModelA();

		System.out.println(WatchUtility.getProducerCountry(da));
		System.out.println(WatchUtility.getProducerCountry(db));
		System.out.println(WatchUtility.getProducerCountry(dw));
		System.out.println(WatchUtility.getProducerCountry(aw));
		System.out.println(WatchUtility.getProducerCountry(w1));
		System.out.println(WatchUtility.getProducerCountry(w2));
		System.out.println(WatchUtility.getProducerCountry(w3));

	}

	private static void case4() {

		DigitalWatch dw = new DigitalWatch();

		if (dw instanceof DigiModelA) {
			System.out.println("dw is-a DigiModelA");
		} else {
			System.out.println("dw is-not-a DigiModelA");
		}
	}

}

Leave a Reply

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