java:inheritance

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
Next revision Both sides next revision
java:inheritance [2015/02/13 12:17]
gthanos created
java:inheritance [2016/02/12 15:47]
gthanos [Προσβασιμότητα των κληρονομούμενων πεδίων]
Line 1: Line 1:
 ====== Κληρονομικότητα ====== ====== Κληρονομικότητα ======
  
-Σε προηγούμενη παράγραφοαναφερόμενοι στην κλάση [[:java:class:Κληρονομικότητα ​Κλάσης ​έγινε αναφορά και στην κληρονομικότητα κλάσεων]] στην Java. +Βασικό χαρακτηριστικό του αντικειμενοστραφούς ​προγραμματισμού είναι η δυνατότητα να παράγουμε νέες κλάσεις με βάση υφιστάμενες κλάσεις,​ εξειδικεύοντας και επεκτείνοντας τα χαρακτηριστικά των υφιστάμενων. Η διαδικασία επέκτασης των υφιστάμενων κλάσεων σε νέες ειδικότερες κλάσεις ​ονομάζεται κληρονομικότητα
 + 
 +Κάθε κλάση που κληρονομεί από μία άλλη κλάση ονομάζεται υποκλάση (//​subclass//​) της γονικής κλάσης από την οποία κληρονομεί. Αντίστοιχα, η γονική κλάση ονομάζεται υπερκλάση (//​superclass//​) της κληρονομούμενης ​κλάσης. 
 + 
 +{{  :​java:​super_sub_class.jpg ​ | }} 
 + 
 +Όπως φαίνεται και στο παραπάνω σχήμα μία κλάση (//​subclass//​) μπορεί να κληρονομεί __**ΜΟΝΟ ΜΙΑ**__ άλλη κλάση. Αντίστροφα μία κλάση (//​superclass//​) μπορεί να κληρονομείται από πολλές διαφορετικές κλάσεις. Παρακάτω δίνουμε ένα παράδειγμα κληρονομικότητας από το site της ​[[https://​docs.oracle.com/​javase/​tutorial/​java/​IandI/​subclasses.html|Oracle]],​ όπου η κλάση ''​MountainBike''​ αποτελεί εξειδίκευση της κλάσης ''​Bicycle''​. 
 + 
 +<code java Bicycle.java>​ 
 +public ​class Bicycle { 
 +         
 +    private int cadence; 
 +    private int gear; 
 +    private int speed; 
 +        
 +    public Bicycle(int startCadence,​ int startSpeed, int startGear) { 
 +        gear = startGear;​ 
 +        cadence = startCadence;​ 
 +        speed = startSpeed;​ 
 +    } 
 +     
 +    public int getCadence() { 
 +          return cadence; 
 +    } 
 +     
 +    public int getGear() { 
 +          return gear; 
 +    } 
 +     
 +    public int getSpeed() { 
 +          return speed; 
 +    } 
 + 
 +    public void setCadence(int newValue) { 
 +        cadence = newValue; 
 +    } 
 +         
 +    public void setGear(int newValue) { 
 +        gear = newValue; 
 +    } 
 +         
 +    public void applyBrake(int decrement) { 
 +        speed -= decrement;​ 
 +    } 
 +         
 +    public void speedUp(int increment) { 
 +        speed += increment;​ 
 +    } 
 +         
 +
 +</​code>​ 
 + 
 +<code java MountainBike.java>​ 
 +public class MountainBike extends Bicycle { 
 + 
 +    private int seatHeight;​ 
 + 
 +    // the MountainBike subclass has 
 +    // one constructor 
 +    public MountainBike(int startHeight,​ int startCadence,​ 
 +                        int startSpeed, int startGear) { 
 +        super(startCadence,​ startSpeed, startGear);​ 
 +        seatHeight = startHeight;​ 
 +    }    
 +         
 +    // the MountainBike subclass has 
 +    // one method 
 +    public void setHeight(int newValue) { 
 +        seatHeight = newValue; 
 +    } 
 +     
 +    public int getSeatHeight() { 
 +        return seatHeight;​ 
 +    } 
 +
 +</​code>​ 
 + 
 +===== Προσβασιμότητα των κληρονομούμενων πεδίων ===== 
 + 
 +Μία κλάση η οποία κληρονομεί μία άλλη κλάση έχει πρόσβαση στα μέλη (πεδία και μεθόδους) της κλάσης αυτής ως εξής: 
 + 
 +  * Έχει πρόσβαση στα //​**public**//​ και //​**protected**//​ μέλη της γονικής κλάσης 
 +  * Έχει πρόσβαση στα //**package private**// μέλη (δηλ. τα μέλη χωρίς προσδιοριστή πρόσβασης) μόνο αν βρίσκεται στο ίδιο πακέτο με την γονική κλάση. 
 +  * Δεν έχει πρόσβαση στα //​**private**//​ μέλη της κλάσης.  
 + 
 +Αν υπάρχουν //​**public**//​ μέθοδοι οι οποίες επιτρέπουν την πρόσβαση σε //​**private**//​ πεδία, τότε αυτές μπορούν να χρησιμοποιηθούν για τον ορισμό ή για την λήψη της τιμής τους. 
 + 
 +===== Τι μπορούμε να κάνουμε σε μία υποκλάση... ===== 
 + 
 +  * Να χρησιμοποιήσουμε τα πεδία της γονικής κλάσης στα οποία ​έχουμε πρόσβαση (public, protected, package-private στο ίδιο package). 
 +  * Να ορίσουμε νέα πεδία. 
 +  * Να ορίσουμε νέα πεδία που να έχουν ίδιο όνομα με πεδία της ​γονικής κλάσης. Σε αυτή την περίπτωση "​κρύβουμε"​ τα πεδία της γονικής κλάσης. Η συγκεκριμένη πρακτική δεν συνίσταται και παραπέμπει σε λανθασμένη σχεδίαση κώδικα. 
 +  * Να χρησιμοποιήσουμε τις μεθόδους της γονικής κλάσης στις οποίες έχουμε πρόσβαση (public, protected, package-private στο ίδιο package). 
 +  * Μπορούμε να γράψουμε νέες στατικές ή μη στατικές μεθόδους για τη υποκλάση. 
 +  * Μπορούμε να γράψουμε νέες μεθόδους που έχουν το ίδιο //​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 | Κληρονομικότητα πολλαπλών γονικών κλάσεων ]]|
  
java/inheritance.txt · Last modified: 2016/03/31 08:25 by doufexi