Polymorfismi Java-opetusohjelmassa - olio-ohjelmoinnin esimerkkikoodilla

Polymorfismi antaa mahdollisuuden käsitellä esineitä korvattavalla tavalla. Tämä vähentää koodin päällekkäisyyttä, kun haluat, että samat toiminnot suoritetaan erityyppisille kohteille. Polymorfismi tarkoittaa kirjaimellisesti " monia muotoja ".

Selitetään, mitä tarkoitamme tällä tarkalleen.

Selitys polymorfismista analogisesti

Jos olet koskaan matkustanut kansainvälisesti, yksi pakkausluettelosi kohde on todennäköisesti sähköpistokesovitin. Muuten et ehkä voi ladata puhelinta tai muita laitteita.

pakkaus.jpg

Outoa, että maailmassa on noin 16 erityyppistä pistorasiaa. Joillakin on 2 nastaa, joillakin 3 nastaa, joillakin nastoilla on pyöreä muoto, joillakin nastoilla on suorakaiteen muotoisuus ja nastojen kokoonpano vaihtelee.

Useimpien ihmisten ratkaisu on ostaa yleispistokesovitin.

Jos haluat tarkastella ongelmaa toisella tavalla, kysymys on yleensä siitä, että meillä on pistorasiarajapinta, joka hyväksyy vain yhden tyyppisen plug-olion! Pistorasiat eivät ole polymorfisia.

Elämä olisi paljon helpompaa kaikille, jos meillä olisi pistorasioita, jotka voisivat hyväksyä monia erityyppisiä pistokkeita. Voimme tehdä pistorasiarajapinnan polymorfiseksi luomalla eri muotoisia rakoja. Alla olevasta kuvasta näet, miten tämä on tehty.

pistorasian metafora

Polymorfismi auttaa meitä luomaan yleisempiä rajapintoja.

Selitys koodilla

Kaikkia esineitä, joilla on IS-A-suhde, pidetään polymorfisina. Sinulla on IS-A-suhde perimisen kautta (käyttämällä luokan allekirjoituksessa laajennettua avainsanaa) tai rajapintojen kautta (käyttämällä luokan allekirjoituksessa toteutettua avainsanaa).

Polymorfismin ymmärtämiseksi kokonaan, sinun tulee ymmärtää myös perintö ja rajapinnat.

class Dog extends Animal implements Canine{ // ... some code here } 

Perustuen katkelmassa mainittiin, Dogon seuraava on A suhteet: Animal, Canineja Object(jokaisella luokalla implisiittisesti perii Object-luokan, joka kuulostaa hieman naurettavaa!).

Annetaan yksinkertainen (typerä) esimerkki havainnollistamaan kuinka voimme käyttää polymorfismia yksinkertaistamaan koodia. Haluamme luoda sovelluksen, jossa on kyselylaite, joka voi vakuuttaa kaikki eläimet puhumaan.

kuulustelu

Luomme Interrogatorluokan, joka on vastuussa eläinten vakuuttamisesta puhumaan. Emme halua kirjoittaa menetelmä kullekin eläimelle: convinceDogToTalk(Dog dog), convinceCatToTalk(Cat cat)ja niin edelleen.

Haluaisimme yhden yleisen menetelmän, joka hyväksyisi kaikki eläimet. Kuinka voimme tehdä tämän?

class Interrogator{ public static void convinceToTalk(Animal subject) { subject.talk(); } } // We don't want anyone creating an animal object! abstract class Animal { public abstract void talk(); } class Dog extends Animal { public void talk() { System.out.println("Woof!"); } } class Cat extends Animal { public void talk() { System.out.println("Meow!"); } } public class App { public static void main(String[] args){ Dog dog = new Dog(); Cat cat = new Cat(); Animal animal = new Dog(); Interrogator.convinceToTalk(dog); //prints "Woof!" Interrogator.convinceToTalk(cat); //prints "Meow!" Interrogator.convinceToTalk(animal); //prints "Woof!" } } 

Luomme convinceToTalkmenetelmän Animalobjektin hyväksymiseksi parametrina. Menetelmän sisällä kutsumme talkkyseisen objektin menetelmää. Niin kauan kuin objektityyppi on Animaltai alaluokka Animal, kääntäjä on onnellinen.

Java Virtual Machine (JVM) päättää ajon aikana, mitä menetelmää kutsutaan objektiluokan perusteella. Jos objektilla on tyypin tyyppi Dog, JVM käynnistää toteutuksen, joka sanoo "Vau!".

Tämä maksaa itsensä kahdella tavalla:

  1. Meidän on kirjoitettava vain yksi yleinen menetelmä. Meidän ei tarvitse tehdä minkäänlaista tyyppitarkistusta.
  2. Jos luomme tulevaisuudessa uuden eläintyypin, meidän ei tarvitse muuttaa Interrogatorluokkaa.

Tämän tyyppistä polymorfismia kutsutaan ensisijaiseksi.

Korvaava

Käsittelemämme esimerkki kattoi jo laajan käsitteen ohittamisesta. Annetaan muodollinen määritelmä ja lisää yksityiskohtia.

Ohittaminen on, kun luot toisen toteutustavan täsmälleen samasta ilmentymämenetelmästä (identtinen menetelmäallekirjoitus) liittyvään luokkaan.

Suorituksen aikana valitaan objektityypin menetelmä . Siksi ohittamista kutsutaan myös ajonaikaiseksi polymorfismiksi.

Korvaus saavutetaan tarjoamalla menetelmä eri tavalla lapsiluokassa (alaluokka), joka on määritelty sen vanhempaluokassa (yläluokka).

ensisijainen perintö

Ohittaminen saavutetaan myös tarjoamalla rajapinnassa määritellyn menetelmän erilaisia ​​toteutuksia.

ensisijainen käyttöliittymä

Menetelmän ohittamista koskevat säännöt:

  1. Sen on oltava menetelmä, joka on määritelty IS-A-suhteen kautta (kautta extendstai implements). Siksi saatat löytää sen kutsutaan alatyypin polymorfismiksi.
  2. Sillä on oltava sama argumenttiluettelo kuin alkuperäisellä menetelmämäärityksellä.
  3. Sillä on oltava sama palautustyyppi tai palautustyyppi, joka on alkuperäisen menetelmän määrittelyn palautustyypin alaluokka.
  4. Sillä ei voi olla rajoittavampaa pääsynmuokkausta.
  5. Sillä voi olla vähemmän rajoittava pääsymuuttuja.
  6. Se ei saa aiheuttaa uutta tai laajempaa tarkistettua poikkeusta.
  7. Se voi heittää kapeampi, vähemmän tai ei lainkaan tarkistettuja poikkeuksia, esimerkiksi menetelmä, joka julistaa IOExceptionin, voidaan ohittaa menetelmällä, joka julistaa FileNotFoundExceptionin (koska se on IOException- alaluokka ).
  8. Korvaava menetelmä voi heittää minkä tahansa tarkistamattoman poikkeuksen riippumatta siitä, julistaako ohitettu menetelmä poikkeuksen.

Suositus: Käytä @override- merkintää ohittaessasi menetelmiä. Se tarjoaa kääntöaikaisen virhetarkistuksen menetelmän allekirjoituksessa. Tämä auttaa sinua välttämään yllä lueteltujen sääntöjen rikkomista.

ohittaa merkinnän

Korvaamisen kieltäminen

Jos et halua menetelmän ohittavan, julista se lopulliseksi.

class Account { public final void withdraw(double amount) { double newBalance = balance - amount; if(newBalance > 0){ balance = newBalance; } } } 

Staattiset menetelmät

Staattista menetelmää ei voi ohittaa . Olet todella luomassa itsenäisen määritelmän menetelmälle liittyvässä luokassa.

class A { public static void print() { System.out.println("in A"); } } class B extends A { public static void print() { System.out.println("in B"); } } class Test { public static void main(String[] args) { A myObject = new B(); myObject.print(); // prints “in A” } } 

Running Testluokan yllä olevassa esimerkissä tulostaa "A". Tämä osoittaa, että ohittamista ei tapahdu täällä.

If you change the print method in classes A and B to be an instance method by removing static from the method signature, and run the Test class again, it will print "in B" instead! Overriding is happening now.

Remember, overriding choses the method based on the object type, not the variable type. ?

Overloading (functional polymorphism)

Overloading is when you create different versions of the same method.

The name of the method must be the same, but we can change the parameters

and return type.

In Java's Math class, you will find many examples of overloaded methods. The max method is overloaded for different types. In all cases, it is returning the number with the highest value from the 2 values provided, but it does it for different (unrelated) number types.

ylikuormitus-max-esimerkki

The (reference) variable type is what determines which overloaded method will be chosen. Overloading is done at compile time.

Overloaded methods provide more flexibility for people using your class. People using your class may have data in different formats, or may have different data available to them depending on different situations in their application.

For example, the List class overloads the remove method. A List is an ordered collection of objects. So, you may want to remove an object at a particular position (index) in a list. Or you may not know the position, and just want to remove the object wherever it is. So that's why it has 2 versions.

luettelo-ylikuormitetut -menetelmät

Constructors can be overloaded also.

For example, the Scanner class has many different inputs that can be provided for creating an object. Below is a small snapshot of the constructors that cater to this.

constructor

Rules for overloading a method:

  1. It must have a different argument list.
  2. It may have a different return type.
  3. It may have different access modifiers.
  4. It may throw different exceptions.
  5. Methods from a superclass can be overloaded in a subclass.

Differences between overriding and overloading

  1. Overriding must be based on a method from an IS-A relationship, overloading doesn't have to be. Overloading can occur within a single class.
  2. Overridden methods are chosen based on the object type, whereas overloaded methods are chosen based on the (reference) variable type.
  3. Overriding occurs at run-time, while overloading occurs at compile-time.

Parametric polymorphism

Parameteric polymorphism is achieved through generics in Java.

Generics were added to the language in version 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety".

Basically, a generic form of a class or method can have all of its types replaced.

A simple example is ArrayList. The class definition has a generic in it, and it is signified by . Some of the instance methods such as add use this generic type in their signatures.

arraylist-luokan määritelmä

arraylist definition -metodit

By providing a type in angle brackets when we create an ArrayList object, we fill in the generic references defined throughout the class. So, if we create an ArrayList with the Dog generic type, the add method will only accept a Dog object as an argument.

arraylist-koiramenetelmän allekirjoitus

There is a compile-time error if you try to add anything other than a Dog! If you use a code editor such as IntelliJ, you will get the red squiggly line to highlight your offense (as below).

arraylist-tyypin tarkistus

Final Words

Polymorfismi on hankala aihe käsiteltäväksi, varsinkin kun olet uusi ohjelmoija. Oikeiden tilanteiden tunnistaminen vie jonkin aikaa käyttääksesi sitä koodissasi.

Mutta kun olet tyytyväinen siihen, huomaat, että se parantaa koodiasi paljon.

Photo Attribution

Lippuvalokuva: Markus Spiske Unsplashista.