Java'da çok biçimlilik ve kalıtım

Efsane Venkat Subramaniam'a göre çok biçimlilik, nesne yönelimli programlamadaki en önemli kavramdır. Çok biçimlilik - veya bir nesnenin türüne göre özel eylemler yürütme yeteneği - Java kodunu esnek kılan şeydir. Komuta, Gözlemci, Dekoratör, Strateji ve Dörtlü Çete tarafından oluşturulan diğerleri gibi tasarım desenlerinin tümü bir tür polimorfizm kullanır. Bu konsepte hakim olmak, programlama zorluklarına yönelik çözümler aracılığıyla düşünme yeteneğinizi büyük ölçüde geliştirir.

Kodu alın

Bu meydan okuma için kaynak kodunu alabilir ve buradan kendi testlerinizi yapabilirsiniz: //github.com/rafadelnero/javaworld-challengers

Polimorfizmde arayüzler ve kalıtım

Bu Java Challenger ile, çok biçimlilik ve kalıtım arasındaki ilişkiye odaklanıyoruz. Akılda tutulması gereken en önemli şey, polimorfizmin kalıtım veya arayüz uygulaması gerektirmesidir . Bunu Duke ve Juggy'nin yer aldığı aşağıdaki örnekte görebilirsiniz:

 public abstract class JavaMascot { public abstract void executeAction(); } public class Duke extends JavaMascot { @Override public void executeAction() { System.out.println("Punch!"); } } public class Juggy extends JavaMascot { @Override public void executeAction() { System.out.println("Fly!"); } } public class JavaMascotTest { public static void main(String... args) { JavaMascot dukeMascot = new Duke(); JavaMascot juggyMascot = new Juggy(); dukeMascot.executeAction(); juggyMascot.executeAction(); } } 

Bu kodun çıktısı:

 Punch! Fly! 

Çünkü kendi özel uygulamaları, hem Dukeve Juggy'eylemlerine çalıştırılacaktır.

Metot polimorfizmi aşırı mı yüklüyor?

Pek çok programcının polimorfizmin yöntemi geçersiz kılma ve aşırı yöntem yüklemesi ile ilişkisi konusunda kafası karışıktır. Aslında, tek yöntemi geçersiz kılan gerçek polimorfizmdir. Aşırı yükleme aynı yöntemin adını paylaşır ancak parametreler farklıdır. Çok biçimlilik geniş bir terimdir, bu nedenle bu konuyla ilgili her zaman tartışmalar olacaktır.

Polimorfizmin amacı nedir?

Polimorfizmi kullanmanın en büyük avantajı ve amacı, istemci sınıfını uygulama kodundan ayırmaktır. İstemci sınıfı, sabit kodlanmış olmak yerine, gerekli eylemi yürütmek için uygulamayı alır. Bu şekilde, istemci sınıfı eylemlerini gerçekleştirecek kadar bilir, bu da gevşek birleştirme örneğidir.

Polimorfizmin amacını daha iyi anlamak için şunlara bir göz atın SweetCreator:

 public abstract class SweetProducer { public abstract void produceSweet(); } public class CakeProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cake produced"); } } public class ChocolateProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Chocolate produced"); } } public class CookieProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cookie produced"); } } public class SweetCreator { private List sweetProducer; public SweetCreator(List sweetProducer) { this.sweetProducer = sweetProducer; } public void createSweets() { sweetProducer.forEach(sweet -> sweet.produceSweet()); } } public class SweetCreatorTest { public static void main(String... args) { SweetCreator sweetCreator = new SweetCreator(Arrays.asList(new CakeProducer(), new ChocolateProducer(), new CookieProducer())); sweetCreator.createSweets(); } } 

Bu örnekte, SweetCreatorsınıfın yalnızca sınıfı bildiğini görebilirsiniz  SweetProducer . Her birinin uygulanışını bilmiyor Sweet. Bu ayrım bize sınıflarımızı güncelleme ve yeniden kullanma esnekliği sağlar ve kodun bakımını çok daha kolay hale getirir. Kodunuzu tasarlarken her zaman onu olabildiğince esnek ve sürdürülebilir hale getirmenin yollarını arayın. polimorfizm, bu amaçlar için kullanılacak çok güçlü bir tekniktir.

İpucu : @OverrideEk açıklama, programcıyı geçersiz kılınması gereken aynı yöntem imzasını kullanmak zorunda bırakır. Yöntem geçersiz kılınmazsa, bir derleme hatası olacaktır.

Yöntemin geçersiz kılınmasında kovaryant dönüş türleri

Bir kovaryant tür ise geçersiz kılınan bir yöntemin dönüş türünü değiştirmek mümkündür. Bir kovaryant türü , temelde dönüş türünün bir alt sınıfıdır. Bir örnek düşünün:

 public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } } 

Çünkü Dukea JavaMascot, geçersiz kılma sırasında dönüş türünü değiştirebiliriz.

Çekirdek Java sınıflarıyla çok biçimlilik

Temel Java sınıflarında her zaman polimorfizm kullanıyoruz. Çok basit bir örnek, arayüzü bir tür olarak ArrayListbildiren sınıfı   başlattığımızda List:

 List list = new ArrayList(); 

Daha ileri gitmek için, Java Collections API'yi çok biçimlilik olmadan kullanarak bu kod örneğini düşünün :

 public class ListActionWithoutPolymorphism { // Example without polymorphism void executeVectorActions(Vector vector) {/* Code repetition here*/} void executeArrayListActions(ArrayList arrayList) {/*Code repetition here*/} void executeLinkedListActions(LinkedList linkedList) {/* Code repetition here*/} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList copyOnWriteArrayList) { /* Code repetition here*/} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector()); listAction.executeArrayListActions(new ArrayList()); listAction.executeLinkedListActions(new LinkedList()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList()); } 

Çirkin kod, değil mi? Onu korumaya çalıştığınızı hayal edin! Şimdi aynı örneği inceleyelim ile polimorfizmi:

 public static void main(String … polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List list) { // Execute actions with different lists } } public class ListActionInvoker { public static void main(String... masterPolymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(new Vector()); listAction.executeListActions(new ArrayList()); listAction.executeListActions(new LinkedList()); listAction.executeListActions(new CopyOnWriteArrayList()); } } 

Polimorfizmin yararı esneklik ve genişletilebilirliktir. Birkaç farklı yöntem oluşturmak yerine, genel Listtürü alan tek bir yöntem bildirebiliriz .

Polimorfik bir yöntem çağrısında belirli yöntemleri çağırma

Bir polimorfik çağrıda belirli yöntemleri çağırmak mümkündür, ancak bunu yapmak esneklik pahasına gelir. İşte bir örnek:

 public abstract class MetalGearCharacter { abstract void useWeapon(String weapon); } public class BigBoss extends MetalGearCharacter { @Override void useWeapon(String weapon) { System.out.println("Big Boss is using a " + weapon); } void giveOrderToTheArmy(String orderMessage) { System.out.println(orderMessage); } } public class SolidSnake extends MetalGearCharacter { void useWeapon(String weapon) { System.out.println("Solid Snake is using a " + weapon); } } public class UseSpecificMethod { public static void executeActionWith(MetalGearCharacter metalGearCharacter) { metalGearCharacter.useWeapon("SOCOM"); // The below line wouldn't work // metalGearCharacter.giveOrderToTheArmy("Attack!"); if (metalGearCharacter instanceof BigBoss) { ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!"); } } public static void main(String... specificPolymorphismInvocation) { executeActionWith(new SolidSnake()); executeActionWith(new BigBoss()); } } 

Burada kullandığımız teknik , çalışma zamanında nesne türünü yayınlamak veya kasıtlı olarak değiştirmektir.

Belirli bir yöntemi yalnızca genel türü belirli bir türe dönüştürürken çağırmanın mümkün olduğunu unutmayın . İyi bir benzetme derleyiciye açıkça şunu söylemek olabilir: "Hey, burada ne yaptığımı biliyorum, bu yüzden nesneyi belirli bir türe çevirip belirli bir yöntem kullanacağım."  

Yukarıdaki örneğe başvurulursa, derleyicinin belirli yöntem çağrısını kabul etmeyi reddetmesinin önemli bir nedeni vardır: iletilen sınıf olabilir SolidSnake. Bu durumda, derleyici her alt sınıfı sağlamak için hiçbir yolu yoktur MetalGearCharactergelmiştir giveOrderToTheArmyyöntem ilan etti.

instanceofayrılmış anahtar

Ayrılmış kelimeye dikkat edin instanceof. Belirli bir yöntemi çağırmadan önce MetalGearCharacter, " instanceof" olup olmadığını sorduk BigBoss. O takdirde değildi bir BigBossörneği, aşağıdaki istisna iletisi:

 Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss 

superayrılmış anahtar

Bir Java üst sınıfından bir niteliğe veya yönteme başvurmak istersek ne olur? Bu durumda superrezerve edilmiş kelimeyi kullanabiliriz . Örneğin:

 public class JavaMascot { void executeAction() { System.out.println("The Java Mascot is about to execute an action!"); } } public class Duke extends JavaMascot { @Override void executeAction() { super.executeAction(); System.out.println("Duke is going to punch!"); } public static void main(String... superReservedWord) { new Duke().executeAction(); } } 

Using the reserved word super in Duke’s executeAction method  invokes the superclass method.  We then execute the specific action from Duke. That’s why we can see both messages in the output below:

 The Java Mascot is about to execute an action! Duke is going to punch! 

Take the polymorphism challenge!

Let’s try out what you’ve learned about polymorphism and inheritance. In this challenge, you’re given a handful of methods from Matt Groening’s The Simpsons, and your challenge is to deduce what the output for each class will be. To start, analyze the following code carefully:

 public class PolymorphismChallenge { static abstract class Simpson { void talk() { System.out.println("Simpson!"); } protected void prank(String prank) { System.out.println(prank); } } static class Bart extends Simpson { String prank; Bart(String prank) { this.prank = prank; } protected void talk() { System.out.println("Eat my shorts!"); } protected void prank() { super.prank(prank); System.out.println("Knock Homer down"); } } static class Lisa extends Simpson { void talk(String toMe) { System.out.println("I love Sax!"); } } public static void main(String... doYourBest) { new Lisa().talk("Sax :)"); Simpson simpson = new Bart("D'oh"); simpson.talk(); Lisa lisa = new Lisa(); lisa.talk(); ((Bart) simpson).prank(); } } 

What do you think? What will the final output be? Don’t use an IDE to figure this out! The point is to improve your code analysis skills, so try to determine the output for yourself.

Choose your answer and you’ll be able to find the correct answer below.

 A) I love Sax! D'oh Simpson! D'oh B) Sax :) Eat my shorts! I love Sax! D'oh Knock Homer down C) Sax :) D'oh Simpson! Knock Homer down D) I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down 

What just happened? Understanding polymorphism

For the following method invocation:

 new Lisa().talk("Sax :)"); 

the output will be “I love Sax!” This is  because we are passing a String to the method and Lisa has the method.

For the next invocation:

 Simpson simpson = new Bart("D'oh");

simpson.talk();

The output will be "Eat my shorts!" This is because we’re instantiating  the Simpson type with Bart.

Now check this one, which is a little trickier:

 Lisa lisa = new Lisa(); lisa.talk(); 

Here, we are using method overloading with inheritance. We are not passing anything to the talk method, which is why the Simpson talk method is invoked.  In this case the output will be:

 "Simpson!" 

Here’s one more:

 ((Bart) simpson).prank(); 

In this case, the prank String was passed when we instantiated the Bart class with new Bart("D'oh");. In this case,  first the super.prank method will be invoked, followed by the specific prank method from Bart. The output will be:

 "D'oh" "Knock Homer down" 

Video challenge! Debugging Java polymorphism and inheritance

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the Java polymorphism challenge:

Common mistakes with polymorphism

It’s a common mistake to think it’s possible to invoke a specific method without using casting.

Another mistake is being unsure what method will be invoked when instantiating a class polymorphically. Remember that the method to be invoked is the method of the created instance.

Also remember that method overriding is not method overloading.

It’s impossible to override a method if the parameters are different. It is possible to change the return type of the overridden method if the return type is a subclass of the superclass method.

Polimorfizm hakkında ne hatırlanmalı

  • Oluşturulan örnek, polimorfizm kullanılırken hangi yöntemin çağrılacağını belirleyecektir.
  • @OverrideAçıklama geçersiz kılınmış bir yöntemi kullanmak için programcı mecbur; değilse, bir derleyici hatası olacaktır.
  • Polimorfizm normal sınıflar, soyut sınıflar ve arayüzlerle kullanılabilir.
  • Çoğu tasarım modeli, bir çeşit polimorfizme bağlıdır.
  • Polimorfik alt sınıfınızda belirli bir yöntemi kullanmanın tek yolu çevrim kullanmaktır.
  • Polimorfizm kullanarak kodunuzda güçlü bir yapı tasarlamak mümkündür.
  • Testlerinizi çalıştırın. Bunu yaparak, bu güçlü konseptte ustalaşabileceksiniz!

Cevap anahtarı

Bu Java rakibi cevabı D . Çıktı şu şekilde olacaktır:

 I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down 

"Java'da polimorfizm ve miras" adlı bu hikaye, orijinal olarak JavaWorld tarafından yayınlandı.