This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
java:inheritance [2015/02/13 13:01] gthanos |
java:inheritance [2016/02/12 14:28] gthanos |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Κληρονομικότητα ====== | ====== Κληρονομικότητα ====== | ||
- | Σε προηγούμενη παράγραφο, αναφερόμενοι στην κλάση έγινε [[:java:class#Κληρονομικότητα Κλάσης | αναφορά στην κληρονομικότητα κλάσεων]] στην Java. Κάθε κλάση που κληρονομεί από μία άλλη κλάση ονομάζεται υποκλάση (//subclass//) της γονικής κλάσης από την οποία κληρονομεί. Αντίστοιχα, η γονική κλάση ονομάζεται υπερκλάση (//superclass//) της κληρονομούμενης κλάσης. | + | Βασικό χαρακτηριστικό του αντικειμενοστραφούς προγραμματισμού είναι η δυνατότητα να παράγουμε νέες κλάσεις με βάση υφιστάμενες κλάσεις, εξειδικεύοντας και επεκτείνοντας τα χαρακτηριστικά των υφιστάμενων. Η διαδικασία επέκτασης των υφιστάμενων κλάσεων σε νέες ειδικότερες κλάσεις ονομάζεται κληρονομικότητα. |
- | {{ :java:super_sub_class.jpg | }} | + | Κάθε κλάση που κληρονομεί από μία άλλη κλάση ονομάζεται υποκλάση (//subclass//) της γονικής κλάσης από την οποία κληρονομεί. Αντίστοιχα, η γονική κλάση ονομάζεται υπερκλάση (//superclass//) της κληρονομούμενης κλάσης. |
+ | {{ :java:super_sub_class.jpg | }} | ||
- | Όπως φαίνεται και στο παραπάνω σχήμα μία κλάση (//subclass//) μπορεί να κληρονομεί __**ΜΟΝΟ ΜΙΑ**__ άλλη κλάση. Αντίστροφα μία κλάση (//superclass//) μπορεί να κληρονομεί πολλές διαφορετικές κλάσεις. Παρακάτω δίνουμε το παράδειγμα κληρονομικότητας που παρουσιάσαμε νωρίτερα ελαφρά παραλλαγμένο. | + | Όπως φαίνεται και στο παραπάνω σχήμα μία κλάση (//subclass//) μπορεί να κληρονομεί __**ΜΟΝΟ ΜΙΑ**__ άλλη κλάση. Αντίστροφα μία κλάση (//superclass//) μπορεί να κληρονομείται από πολλές διαφορετικές κλάσεις. Παρακάτω δίνουμε ένα παράδειγμα κληρονομικότητας όπου η κλάση ''MountainBike'' αποτελεί εξειδίκευση της κλάσης ''Bicycle''. |
<code java Bicycle.java> | <code java Bicycle.java> | ||
Line 96: | Line 97: | ||
* Μπορούμε να γράψουμε νέες μεθόδους που έχουν το ίδιο //signature// (ίδιο όνομα, ίδιο αριθμό και ίδιο τύπο ορισμάτων), ώστε να επαναορίσουμε (//override//) τις μεθόδους αυτές στην υποκλάση. Η πρακτική αυτή είναι συνήθης. | * Μπορούμε να γράψουμε νέες μεθόδους που έχουν το ίδιο //signature// (ίδιο όνομα, ίδιο αριθμό και ίδιο τύπο ορισμάτων), ώστε να επαναορίσουμε (//override//) τις μεθόδους αυτές στην υποκλάση. Η πρακτική αυτή είναι συνήθης. | ||
* Μπορούμε να γράψουμε νέες στατικές (//static//) μεθόδους που έχουν το ίδιο //signature//, ώστε να επαναορίσουμε (//override//) τις μεθόδους αυτές στην υποκλάση. Επαναορίζοντας στατικές μεθόδους, κρύβουμε τις αντίστοιχες μεθόδους της γονικής κλάσης. | * Μπορούμε να γράψουμε νέες στατικές (//static//) μεθόδους που έχουν το ίδιο //signature//, ώστε να επαναορίσουμε (//override//) τις μεθόδους αυτές στην υποκλάση. Επαναορίζοντας στατικές μεθόδους, κρύβουμε τις αντίστοιχες μεθόδους της γονικής κλάσης. | ||
- | * | + | * Μπορούμε να γράψουμε κατασκευαστές της υποκλάσης που χρησιμοποιούν κατασκευαστές της γονικής κλάσης. |
+ | |||
+ | ===== Implicit και Explicit Casting of Objects ===== | ||
+ | |||
+ | Σε συνέχεια του προηγούμενου παραδείγματος κληρονομικότητας μπορούμε να γράψουμε | ||
+ | |||
+ | <code java> | ||
+ | MountainBike myBike = new MountainBike(); | ||
+ | </code> | ||
+ | |||
+ | Από την παραπάνω δήλωση η μεταβλητή ''myBike'' είναι τύπου ''MountainBike''. Επειδή όμως ο τύπος ''MountainBike'' κληρονομεί από την μεταβλητή ''Bicycle'' η συγκεκριμένη μεταβλητή είναι και τύπου Bicycle. Επομένως θα μπορούσαμε να γράψουμε | ||
+ | |||
+ | <code java> | ||
+ | Bicycle myBicycle = myBike; | ||
+ | //ή | ||
+ | Βicycle yourBicycle = new MountainBike(); | ||
+ | </code> | ||
+ | |||
+ | Την παραπάνω ανάθεση την ονομάζουμε //Implicit Casting// διότι αναθέτουμε μία μεταβλητή ενός τύπου δεδομένων (''myBike'') σε μία μεταβλητή γονικού τύπου δεδομένων (''myBicycle''), χωρίς type casting. | ||
+ | |||
+ | Ας δοκιμάσουμε το ανάποδο παράδειγμα τώρα | ||
+ | |||
+ | <code java> | ||
+ | Bycycle myBicycle = new Bicycle(); | ||
+ | MountainBike myBike = myBicycle; | ||
+ | </code> | ||
+ | |||
+ | Σε αυτή την περίπτωση ο compiler διαμαρτύρεται, διότι η μεταβλητή ''myBicycle'' είναι τύπου ''Bicycle'' και δεν είναι απαραίτητο ότι είναι και τύπου ''MountainBike''. Αν θέλουμε να ξεπεράσουμε το παραπάνω πρόβλημα θα πρέπει να γράψουμε το εξής: | ||
+ | |||
+ | <code java> | ||
+ | Bycycle myBicycle = new Bicycle(); | ||
+ | MountainBike myBike = (MountainBike) myBicycle; | ||
+ | </code> | ||
+ | |||
+ | Εδώ ενημερώνουμε τον compiler ότι η μεταβλητή ''myBicycle'' είναι και τύπου ''MountainBike'', λαμβάνοντας ο προγραμματιστής την ευθύνη ότι κάτι τέτοιο ισχύει. Εάν δεν ισχύει κάτι τέτοιο κατά την εκτέλεση του προγράμματος θα παραχθεί μία εξαίρεση (exception)*. Θα δούμε πιο κάτω τι είναι και πως διαχειριζόμαστε τις εξαιρέσεις. | ||
+ | |||
+ | Ένας πιο ασφαλής τρόπος για να επαναλάβουμε τον παραπάνω κώδικα, χωρίς να παραχθεί εξαίρεση είναι ο εξής: | ||
+ | |||
+ | <code java> | ||
+ | Bycycle myBicycle = new Bicycle(); | ||
+ | MountainBike myBike; | ||
+ | if (myBicycle instanceof MountainBike) { | ||
+ | myBike = (MountainBike)myBicycle; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===== Final Κλάσεις και Μέθοδοι ===== | ||
+ | |||
+ | Μπορείτε να δηλώσετε μία ή περισσότερες μεθόδους μία κλάσης ως //final//. Δηλώνοντας μία μέθοδο ως final η μέθοδος αυτή δεν μπορεί να επαναοριστεί σε μία υποκλάση της κλάσης αυτής. Ο συχνότερος λόγος για να δηλώσετε μία μέθοδο ως //final// είναι αν η υλοποίηση της μεθόδου δεν πρέπει να αλλάξει. Ένα παράδειγμα είναι το παρακάτω: | ||
+ | |||
+ | <code java> | ||
+ | class ChessAlgorithm { | ||
+ | enum ChessPlayer { WHITE, BLACK } | ||
+ | ... | ||
+ | final ChessPlayer getFirstPlayer() { | ||
+ | return ChessPlayer.WHITE; | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Γενικότερα, μέθοδοι που καλούνται από τους κατασκευαστές της κλάσης __θα πρέπει__ να ορίζονται ως //final// καθώς αν αλλάζουν την υλοποίηση τους σε υποκλάσεις μπορούν να δημιουργηθούν προβλήματα ως προς την ορθή αρχικοποίηση των μεταβλητών της κλάσης. | ||
+ | |||
+ | Τέλος, μπορείτε να προσδιορίσετε μία κλάση ως //final//, όταν θέλετε να δηλώσετε ότι η συγκεκριμένη κλάση δεν πρέπει να έχει υποκλάσεις. Ένα παράδειγμα τέτοια κλάσης είναι η κλάση [[http://docs.oracle.com/javase/7/docs/api/java/lang/String.html | String]] της standard βιβλιοθήκης της Java. | ||
+ | |Προηγούμενο: [[ :java:interfaces | Διεπαφές ]] | Επόμενο: [[ :java:multiple_inheritance | Κληρονομικότητα πολλαπλών γονικών κλάσεων ]]| | ||