"Amigo, balinaları sever misin?"

"Balinalar mı? Hayır, adlarını hiç duymadım."

"İnek gibi, sadece daha büyük ve yüzüyor. Bu arada, balinalar ineklerden geldi. Ah, ya da en azından ortak bir ataları var. Fark etmez."

Polimorfizm ve geçersiz kılma - 1

"Dinleyin. Size başka bir çok güçlü OOP aracından bahsetmek istiyorum: polimorfizm . Dört özelliği var."

1) Yöntem geçersiz kılma.

Bir oyun için "İnek" sınıfı yazdığınızı hayal edin. Çok sayıda üye değişkeni ve yöntemi vardır. Bu sınıfın nesneleri çeşitli şeyler yapabilir: yürümek, yemek yemek, uyumak. İnekler ayrıca yürürken zil çalar. Diyelim ki sınıftaki her şeyi en ince ayrıntısına kadar uyguladınız.

Polimorfizm ve geçersiz kılma - 2

Sonra aniden müşteri, tüm eylemlerin denizde gerçekleştiği ve ana karakterin bir balina olduğu yeni bir oyun seviyesi yayınlamak istediğini söyler.

Balina sınıfını tasarlamaya başladınız ve bunun İnek sınıfından çok az farklı olduğunu fark ettiniz. Her iki sınıf da çok benzer mantık kullanır ve siz kalıtımı kullanmaya karar verirsiniz.

İnek sınıfı, ana sınıf olmak için idealdir: zaten gerekli tüm değişkenlere ve yöntemlere sahiptir. Tek yapmanız gereken balinanın yüzme yeteneğini eklemek. Ancak bir sorun var: Balinanızın bacakları, boynuzları ve zili var. Sonuçta, Cow sınıfı bu işlevi uygular. Ne yapabilirsin?

Polimorfizm ve geçersiz kılma - 3

Yöntem geçersiz kılma kurtarmaya gelir. Yeni sınıfımızda tam olarak ihtiyacımız olanı yapmayan bir yöntemi miras alırsak, yöntemi başka bir yöntemle değiştirebiliriz.

Polimorfizm ve geçersiz kılma - 4

Bu nasıl yapılır? Torun sınıfımızda, değiştirmek istediğimiz yöntemi bildiririz (üst sınıftakiyle aynı yöntem imzasıyla) . Daha sonra method için yeni kod yazıyoruz. Bu kadar. Sanki üst sınıfın eski yöntemi yokmuş gibi.

İşte nasıl çalıştığı:

kod Tanım
class Cow
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
Burada iki sınıf tanımlıyoruz:  Cow ve  WhaleWhalemiras alır  Cow.

Sınıf  , yöntemi Whale geçersiz kılar  printName();.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Bu kod ekranda « Ben bir ineğim » gösterir .
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Bu kod ekranda « Ben bir balinayım » gösterir.

Miras aldıktan Cowve geçersiz kıldıktan sonra printName, Whalesınıf aslında aşağıdaki verilere ve yöntemlere sahiptir:

kod Tanım
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Herhangi bir eski yöntem hakkında hiçbir şey bilmiyoruz.

"Açıkçası, beklediğim buydu."

2) Ama hepsi bu değil.

Cow Sınıfın diğer iki yöntemi çağıran bir  printAll, yöntemi olduğunu varsayalım. O zaman kod şu şekilde çalışır:"

Ekran şunu gösterecek:
Ben beyazım
Ben bir balinayım

kod Tanım
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Ekran şunu gösterecek:
Ben beyazım
Ben bir balinayım

Bir Whale nesnesinde Cow sınıfının printAll() yöntemi çağrıldığında, Cow'ın değil Whale'ın printName() yönteminin kullanılacağını unutmayın.

Önemli olan yöntemin yazıldığı sınıf değil, yöntemin çağrıldığı nesnenin tipidir (sınıf).

"Anlıyorum."

"Yalnızca statik olmayan yöntemleri devralabilir ve geçersiz kılabilirsiniz. Statik yöntemler miras alınmaz ve bu nedenle geçersiz kılınamaz."

Kalıtımı uyguladıktan ve yöntemleri geçersiz kıldıktan sonra Whale sınıfı şöyle görünür:

kod Tanım
class Whale
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Kalıtımı uyguladıktan ve yöntemi geçersiz kıldıktan sonra Whale sınıfının nasıl göründüğü aşağıda açıklanmıştır. Herhangi bir eski yöntem hakkında hiçbir şey bilmiyoruz printName.

3) Tip döküm.

Burada daha da ilginç bir nokta var. Bir sınıf, üst sınıfının tüm yöntemlerini ve verilerini devraldığından, bu sınıfın bir nesnesine, üst sınıfın değişkenleri (ve üst sınıfın üst öğesi vb., Object sınıfına kadar) tarafından başvurulabilir . Bu örneği göz önünde bulundurun:

kod Tanım
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
Ekran şunu gösterecek:
Ben beyazım.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
Ekran şunu gösterecek:
Ben beyazım.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
Ekran şunu gösterecektir:
Whale@da435a.
toString() yöntemi, Object sınıfından miras alınır.

"Güzel şey. Ama buna neden ihtiyacın olsun ki?"

"Değerli bir özellik. Çok ama çok değerli olduğunu daha sonra anlayacaksın."

4) Geç bağlama (dinamik gönderim).

İşte göründüğü gibi:

kod Tanım
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Ekran şunu gösterecek:
Ben bir balinayım.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
Ekran şunu gösterecek:
Ben bir balinayım.

Hangi belirli printName yöntemini (İnek veya Balina sınıfınınki) çağıracağımızı belirleyen değişkenin türü değil , değişken tarafından başvurulan nesnenin türü olduğuna dikkat edin.

İnek değişkeni , bir Whale nesnesine bir referans depolar ve Whale sınıfında tanımlanan printName yöntemi çağrılır.

"Eh, netlik adına bunu eklemediler."

"Evet, o kadar açık değil. Şu önemli kuralı unutma:"

Bir değişken üzerinde çağırabileceğiniz yöntemler kümesi, değişkenin türüne göre belirlenir. Ancak hangi özel yöntemin/uygulamanın çağrılacağı, değişken tarafından başvurulan nesnenin türü/sınıfı tarafından belirlenir.

"Deneyeceğim."

"Bununla sürekli karşılaşacaksın, bu yüzden çabucak anlayacak ve asla unutmayacaksın."

5) Tip dökümü.

Döküm, referans türleri için, yani sınıflar için, ilkel türler için olduğundan farklı çalışır. Ancak, genişletme ve daraltma dönüştürmeleri referans türleri için de geçerlidir. Bu örneği göz önünde bulundurun:

Genişleyen dönüşüm Tanım
Cow cow = new Whale();

Klasik bir genişletme dönüştürmesi. Artık Whale nesnesinde yalnızca Cow sınıfında tanımlanan yöntemleri çağırabilirsiniz.

Derleyici, inek değişkenini yalnızca İnek türü tarafından tanımlanan yöntemleri çağırmak için kullanmanıza izin verecektir.

Daraltma dönüşümü Tanım
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
Tip kontrolü ile klasik bir daraltma dönüştürmesi . İnek türündeki inek değişkeni , bir Balina nesnesine bir referans depolar.
Durumun böyle olup olmadığını kontrol ediyoruz ve ardından (genişletme) tip dönüştürmesini gerçekleştiriyoruz. Buna tip dökümü de denir .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
Nesnenin tipini kontrol etmeden bir referans tipinin daraltma dönüşümünü de gerçekleştirebilirsiniz.
Bu durumda, inek değişkeni bir Whale nesnesinden başka bir şeye işaret ediyorsa, bir istisna (InvalidClassCastException) atılır.

6) Ve şimdi lezzetli bir şey için. Orijinal yöntemi çağırmak.

Bazen kalıtsal bir yöntemi geçersiz kılarken, onu tamamen değiştirmek istemezsiniz. Bazen sadece biraz eklemek istersin.

Bu durumda, yeni yöntemin kodunun aynı yöntemi, ancak temel sınıfta çağırmasını gerçekten istiyorsunuz. Ve Java bunu yapmanıza izin verir. Bu şekilde yapılır:  super.method().

İşte bazı örnekler:

kod Tanım
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.print("This is false: ");
super.printName();

System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Ekran şunu gösterecek:
Ben beyazım
Bu yanlış: Ben bir ineğim
Ben bir balinayım

"Hmm. Bu bir dersti. Robot kulaklarım neredeyse eridi."

"Evet, bu basit bir şey değil. Karşılaşacağınız en zor materyallerden biri. Profesör diğer yazarların materyallerine bağlantılar sağlayacağına söz verdi, böylece hala bir şey anlamadıysanız, boşluklar."