ΕΙΣΑΓΩΓΗ
Στο σημερινό δωρεάν μάθημα Java θα μιλήσουμε για τον
πολυμορφισμό (polymorphism) και τι ακριβώς εννοούμαι όταν μιλάμε για διαφορετικές μορφές ενός αντικειμένου.
Ακόμα θα χρησιμοποιήσουμε τον instanceof operator για να γνωρίζουμε σε ποια ακριβώς μορφή βρίσκεται το
αντικείμενο που χρησιμοποιούμε. Το σίγουρο είναι ότι σαν προγραμματιστές θα
χρησιμοποιείτε τον πολυμορφισμό σχεδόν σε κάθε κώδικα που γράφετε αφού είναι
ένα από τα πλεονεκτήματα που μας προσφέρει ο αντικειμενοστραφής
προγραμματισμός.
POLYMORPHISM
Στην προηγούμενη ενότητα είχαμε δει την κληρονομικότητα μέσα
από ένα απλό παράδειγμα μιας εφαρμογής που υπολόγιζε τον μισθό των εργαζομένων
ανάλογα αν ήταν full-time ή απλά εξωτερικοί συνεργάτες. Για να αναλύσουμε
καλύτερα την έννοια του πολυμορφισμού, ας προσθέσουμε μια ακόμα κλάση με το
όνομα Manager η οποία και αυτή με την σειρά της θα κληρονομεί από την Salary.
Στην ουσία οι τρεις κλάσεις στις οποίες θα βασιστεί το βασικό μας παράδειγμα
στο σημερινό δωρεάν μάθημα Java είναι οι Employee, Salary και Manager. Ας δούμε
για αρχή πως είναι το καινούργιο UML διάγραμμα της εφαρμογής μας και μετά θα
τρέξουμε ένα παράδειγμα με τις τρεις κλάσεις.
Employee.java
Salary.java
Manager.java
Outsourcing.java
Ο κώδικας της Manager κλάσης
είναι πολύ απλός όπως διαπιστώνετε και εσείς. Προσθέτει 1000 ευρώ επιπλέον στην
μισθοδοσία του εργαζόμενου επειδή σαν Manager δικαιούται ένα bonus.
Ας δούμε και ένα απλό παράδειγμα εκτέλεσης
αυτού του προγράμματος.
EmployeeDemo.java
Output
Hello Michail
Your payment plus your bonus is:
1384.6153846153848
Πριν δούμε την έννοια του
πολυμορφισμού, ας αναλύσουμε άλλη μια φορά τι σημαίνει ακριβώς η γραμμή
Manager m1 = new Manager( );
Δεν θα πω τίποτα καινούργιο, απλά θα
επαναλάβω τον δικό μου απλό ορισμό σε περίπτωση που δεν του είχαμε δώσει αρκετή
σημασία από την ενότητα της κληρονομικότητας. Δεξιά από το ίσον είναι η εντολή
να δημιουργηθεί ένα καινούργιο αντικείμενο κάνοντας χρήση του default
no-argument constructor που μας προσφέρει η java από την στιγμή που δεν έχουμε
δημιουργήσει δικό μας constructor. Οπότε το αντικείμενο είναι ήδη δημιουργημένο
στην μνήμη απλά, επειδή έτσι σχεδιάστηκε η java εξ αρχής, δεν μας επιτρέπει
άμεση πρόσβαση σε αυτό. Για αυτό το λόγο, από τα αριστερά του ίσον,
δημιουργούμε ένα reference το οποίο θα έχει την ευθύνη να καλεί τις μεταβλητές
και τις μεθόδους, που ανήκουν στο αντικείμενο, για μας. Το σημαντικό στοιχείο
είναι το data type του reference m1. Εδώ είναι Manager που σημαίνει ότι το
reference θα συμπεριφέρεται σαν Manager - δηλαδή θα βλέπει όλες τις public
μεταβλητές και τις μεθόδους του αντικειμένου. Αν το δούμε συνολικά λοιπόν θα
μπορούμε να πούμε τον εξής απλό ορισμό: δημιουργούμε ένα αντικείμενο είδος
Manager και το reference συμπεριφέρεται σαν Manager. Δηλαδή θα μπορούσε να
είχαμε δημιουργήσει ένα αντικείμενο είδος Manager αλλά να είχε διαφορετική
συμπεριφορά? Ναι, και αυτός είναι ο πολυμορφισμός.
Ας γυρίσουμε πίσω στην main( ) και ας
δημιουργήσουμε ένα αντικείμενο είδος Manager που να συμπεριφέρεται σαν
Employee.
EmployeeDemo.java
Δημιουργήσαμε ένα αντικείμενο είδος Manager αλλά η
συμπεριφορά του reference είναι ίδια με ενός Employee αντικειμένου. Αυτό μπορούμε
να το επιβεβαιώσουμε εάν γράψουμε m1 και τελεία. Η λίστα με τις μεθόδους και
τις μεταβλητές που θα εμφανιστεί είναι εκείνη της κλάσης Employee χωρίς να
περιλαμβάνει καμία μέθοδο ή μεταβλητή από την κλάση Manager.
Με αυτό τον τρόπο έχουμε τον
έλεγχο πότε ένα αντικείμενο μπορεί να αποκτήσει ολοκληρωμένη πρόσβαση σε όλες
τις μεθόδους και τις public μεταβλητές μιας κλάσης. Αυτή την στιγμή δεν θέλουμε
το αντικείμενο να έχει δυνατότητες πέραν από εκείνες ενός αντικειμένου
Employee. Το αντικείμενο Manager που δημιουργήσαμε, υφίσταται και υπάρχει στην
μνήμη, απλά περιορίζουμε το εύρος δράσης του.
Πριν προχωρήσουμε στην θεωρία του
πολυμορφισμού (polymorphism) πρέπει να αναφέρουμε ότι δεν υφίσταται πολυμορφισμός εάν δεν υπάρχει κληρονομικότητα.
Πρέπει να υπάρχει κληρονομικότητα μεταξύ δύο ή περισσοτέρων κλάσεων για να
μπορέσουμε να παίξουμε με τον πολυμορφισμό.
Επίσης θα πρέπει να γίνει κατανοητό ότι
μπορούμε να δώσουμε οποιαδήποτε συμπεριφορά στο αντικείμενο από την κλάση που
δημιουργήθηκε και πάνω, όχι πιο κάτω από την κλάση που δημιουργήθηκε. Δηλαδή αν
είχαμε δημιουργήσει ένα αντικείμενο είδος Salary θα μπορούσαμε στο reference να
δώσουμε την συμπεριφορά του Employee αλλά όχι εκείνη του Manager.
Οπότε αν δημιουργήσουμε ένα
αντικείμενο είδος Manager θα μπορούσαμε να του δώσουμε την συμπεριφορά του
Employee, Salary ή Manager ενώ εάν δημιουργήσουμε ένα αντικείμενο είδος Salary
τότε μπορούμε να του δώσουμε μόνο την συμπεριφορά του Employee ή εκείνη του
Salary.
Αν έχουμε δώσει την συμπεριφορά του Employee
στο αντικείμενο μας, πως μπορούμε να του αλλάξουμε την συμπεριφορά δίνοντας του
ιδιότητες Salary ή ακόμα και Manager? Η απάντηση είναι απλή – χρησιμοποιώντας casting. Ας δούμε πως μπορούμε να
αλλάξουμε τις δυνατότητες του αντικειμένου δίνοντας του ικανότητες Salary.
EmployeeDemo.java
Η συμπεριφορά του s1 είναι ίδια με εκείνη ενός Salary
αντικειμένου. Θα δούμε ότι εμφανίζονται τώρα τόσο η μεταβλητή salary όσο και η
μέθοδο payment( ). Φυσικά για να κρατήσουμε μόνιμα αυτή την μετατροπή,
αναθέτουμε την καινούργια συμπεριφορά σε μια μεταβλητή s1 είδος Salary.
Με την ίδια λογική, μπορούμε να
κάνουμε το s1 ή το m1 να συμπεριφέρεται σαν Manager. Τότε θα έχουμε αποκτήσει
και απόλυτη πρόσβαση σε όλες τις ιδιότητες και μεταβλητές της κλάσης Manager.
Επειδή η κλάση Manager κληρονομεί από την Salary και εκείνη με την σειρά της
από την Employee, τότε ένα αντικείμενο Manager έχει πρόσβαση σε όλες τις
μεθόδους και μεταβλητές και των τριών κλάσεων.
Output
Your salary+bonus is:
1384.6153846153848
Εδώ μετατρέπουμε το m1 πίσω στην
κλάση από την οποία δημιουργήθηκε αρχικά δίνοντας του πρόσβαση ξανά σε όλες τις
μεθόδους και μεταβλητές της κλάσης Manager. Καλό θα ήταν να μην έχετε πολλά
reference, που να αναφέρονται στο ίδιο αντικείμενο, διαθέσιμα στο πρόγραμμα
σας. Γιατί πολύ απλά, αλλάζοντας κάτι από το ένα reference θα επηρεάσει το άλλο
αφού αναφέρονται στο ίδιο αντικείμενο. Οπότε ο πιο σωστός τρόπος είναι να
αναθέτετε null στο πρώτο reference.
Με αυτό τον τρόπο το reference παύει να δείχνει σε κάποιο αντικείμενο.
INSTANCEOF OPERATOR
Σε όλη αυτή την διαδικασία που μόλις
περιγράψαμε υπάρχει ένα πρόβλημα – δεν έχουμε κάποιο προγραμματιστικό τρόπο να
γνωρίζουμε από πια κλάση γεννήθηκε εξ αρχής το αντικείμενο. Πολλές φορές δεν
είμαστε εμείς που δημιουργούμε αντικείμενα μέσα σε μια μεγάλη εφαρμογή. Απλά τα
δεχόμαστε από κάποιο service ή άλλη διαδικασία. Οπότε δεν θα είμαστε εμείς
εκείνοι που έχουν καλέσει τον constructor. Σκεφτείτε λοιπόν ένα δέντρο
κληρονομικότητας που περιέχει 10 κλάσεις. Και το αντικείμενο έχει γεννηθεί από την
5 κλάση. Πως εμείς θα το γνωρίζουμε αυτό? Αν προσπαθήσετε να μετατρέψετε το
αντικείμενο σε κάτι αντίστοιχο της κλάσης 6 θα λάβετε λάθος γιατί δεν γεννήθηκε
από εκεί, ενώ αν το μετατρέψετε σε εκείνο της κλάσης 4 δεν θα γνωρίζετε αν
έχετε όλες τις δυνατότητες γιατί απλά δεν μπορείτε να επιβεβαιώσετε ότι
δημιουργήθηκε από εκεί. Η λύση λοιπόν σε όλη αυτή την διαδικασία είναι ένας
απλός operator που ονομάζεται instanceof.
Ο instanceof operator απλά ελέγχει από ποια κλάση αρχικά δημιουργήθηκε το
αντικείμενο. Ο τρόπος που το εφαρμόζουμε μέσα στον κώδικα μας είναι απλά με ένα
if-else έλεγχο. Ας δούμε ένα απλό παράδειγμα.
EmployeeDemo.java
Output
Casting to a Manager reference
Your payment is:
1384.6153846153848
Ο βασικός κανόνας σε αυτή την διαδικασία είναι να ξεκινήσουμε
από την πιο χαμηλή στην κληρονομικότητα κλάση και να αναβαίνουμε μια-μια κλάση
μέχρι να φτάσουμε στην αρχική. Αν είχαμε βάλει τις κλάσεις μας ανάποδα στον
κώδικα της EmployeeDemo, τότε θα γινόταν μετατροπή σε αντικείμενο είδος Salary
αφού ένα αντικείμενο Manager είναι και Salary.
Μην ξεχάσετε να κάνετε ένα μικρό donation έτσι ώστε αυτό το blog να μεγαλώσει ακόμα πιο πολύ και να έχει περισσότερες δυνατότητες στην online παράδοση δωρεάν μαθημάτων.
0 Comments
Η γνώμη σας είναι σημαντική.