User Tools

Site Tools


java:inheritance

Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
java:inheritance [2016/02/12 15:45]
gthanos
java:inheritance [2021/04/02 14:19]
gthanos
Line 1: Line 1:
 ====== Κληρονομικότητα ====== ====== Κληρονομικότητα ======
  
-Βασικό χαρακτηριστικό του αντικειμενοστραφούς προγραμματισμού είναι η δυνατότητα να παράγουμε νέες κλάσεις με βάση υφιστάμενες κλάσεις, εξειδικεύοντας και επεκτείνοντας τα χαρακτηριστικά των υφιστάμενων. Η διαδικασία επέκτασης των υφιστάμενων κλάσεων σε νέες ειδικότερες κλάσεις ονομάζεται κληρονομικότητα.+Βασικό χαρακτηριστικό του αντικειμενοστραφούς προγραμματισμού είναι η δυνατότητα να παράγουμε νέες κλάσεις με βάση υφιστάμενες, εξειδικεύοντας και επεκτείνοντας τα χαρακτηριστικά τους. Η διαδικασία επέκτασης των υφιστάμενων κλάσεων σε νέες ειδικότερες κλάσεις ονομάζεται κληρονομικότητα.
  
 Κάθε κλάση που κληρονομεί από μία άλλη κλάση ονομάζεται υποκλάση (//subclass//) της γονικής κλάσης από την οποία κληρονομεί. Αντίστοιχα, η γονική κλάση ονομάζεται υπερκλάση (//superclass//) της κληρονομούμενης κλάσης. Κάθε κλάση που κληρονομεί από μία άλλη κλάση ονομάζεται υποκλάση (//subclass//) της γονικής κλάσης από την οποία κληρονομεί. Αντίστοιχα, η γονική κλάση ονομάζεται υπερκλάση (//superclass//) της κληρονομούμενης κλάσης.
  
-{{  :java:super_sub_class.jpg  | }}+{{  :java:inheritance-1.png  | }}
  
-Όπως φαίνεται και στο παραπάνω σχήμα μία κλάση (//subclass//) μπορεί να κληρονομεί __**ΜΟΝΟ ΜΙΑ**__ άλλη κλάση. Αντίστροφα μία κλάση (//superclass//) μπορεί να κληρονομείται από πολλές διαφορετικές κλάσεις. Παρακάτω δίνουμε ένα παράδειγμα κληρονομικότητας από το site της [[https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html|Oracle]], όπου η κλάση ''MountainBike'' αποτελεί εξειδίκευση της κλάσης ''Bicycle''.+Όπως φαίνεται και στο παραπάνω σχήμα μία κλάση (//subclass//) μπορεί να κληρονομεί __**ΜΟΝΟ ΜΙΑ**__ άλλη κλάση. Αντίστροφα μία κλάση (//superclass//) μπορεί να κληρονομείται από πολλές διαφορετικές κλάσεις. Παρακάτω δίνουμε ένα παράδειγμα κληρονομικότητας ως συνέχεια των προηγούμενων ενοτήτων. Ορίζουμε την κλάση ''BasicRectangle'' η οποία αποτελεί το απλό ορθογώνιο παραλληλόγραμμο που γνωρίσαμε στην αρχή και την κλάση ''Rectangle'' που αποτελεί εξειδίκευση της κλάσης ''BasicRectangle'' ορίζοντας επιπλέον το πεδίο ''origin''.
  
-<code java Bicycle.java> +<code java BasicRectangle.java> 
-public class Bicycle { +public class BasicRectangle {
-         +
-    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() { +  int width; 
-          return cadence+  int height; 
-    }+  
 +  public BasicRectangle(int initWidth, int initHeight) { 
 +    width = initWidth
 +    height = initHeight; 
 +  } 
 + 
 +  public void setWidth(int newWidth ) { 
 +    width = newWidth; 
 +  }
          
-    public int getGear() { +  public void setHeight(int newHeight ) { 
-          return gear+    height = newHeight; 
-    }+  } 
 +   
 +  public int getWidth() { 
 +    return width
 +  }
          
-    public int getSpeed() { +  public int getHeight() { 
-          return speed+    return height; 
-    }+  } 
 +   
 +  int area() { 
 +    return width * height; 
 +  } 
 +   
 +  public String toString() { 
 +    return "width: "+width+", height: "+height; 
 +  } 
 +
 +</code>
  
-    public void setCadence(int newValue) { +<code java Point.java> 
-        cadence newValue+class Point { 
-    } +  int x;   // x coordinate 
-         +  int y;   // y coordinate 
-    public void setGear(int newValue) { +   
-        gear newValue+  public Point(int xPos, int yPos) { 
-    +    xPos
-         +    y = yPos; 
-    public void applyBrake(int decrement) { +  
-        speed -decrement+   
-    +  int getX() { 
-         +    return x; 
-    public void speedUp(int increment) { +  } 
-        speed += increment+   
-    } +  void setX(int xPos) { 
-        +    xPos
 +  
 +   
 +  int getY() { 
 +    return y; 
 +  } 
 +   
 +  void setY(int yPos) { 
 +    yPos
 +  
 +   
 +  public String toString() { 
 +    return "("+x+","+y+")"
 +  }
 } }
 </code> </code>
  
-<code java MountainBike.java> +<code java Rectangle.java> 
-public class MountainBike extends Bicycle +public class Rectangle extends BasicRectangle 
- +     
-    private int seatHeight; +  Point origin
- +     
-    // the MountainBike subclass has +  public Rectangle(int initWidth, int initHeightPoint initOrigin) { 
-    // one constructor +    super(initWidthinitHeight); 
-    public MountainBike(int startHeight, int startCadence, +    origin initOrigin
-                        int startSpeed, int startGear) { +  
-        super(startCadencestartSpeed, startGear); +   
-        seatHeight startHeight+  public Rectangle(int initWidth, int initHeight, int originX, int originY) { 
-       +    super(initWidth, initHeight)
-         +    origin = new Point(originX,originY); 
-    // the MountainBike subclass has +  }
-    // one method +
-    public void setHeight(int newValue) { +
-        seatHeight = newValue+
-    }+
          
-    public int getSeatHeight() { +  public void setOrigin(Point newOrigin) { 
-        return seatHeight+    origin = newOrigin; 
-    }+  } 
 +   
 +  public Point getOrigin() { 
 +    return origin; 
 +  } 
 +    
 +  // Move rectangle origin by dx,dy 
 +  public void moveOrigin(int dx, int dy) { 
 +    origin.setX( origin.getX() + dx ); 
 +    origin.setY( origin.getY() + dy ); 
 +  } 
 +   
 +  public String toString() { 
 +    String str = origin.toString() + " "; 
 +    str = str + super.toString(); 
 +    return str; 
 +  }
 } }
 </code> </code>
  
 +<WRAP tip 80% center round>
 +
 +=== Παρατηρήσεις ===
 +
 +  * Η κλάση //BasicRectangle// έχει τα πεδία //width, height//, ενώ η κλάση //Rectangle// διαθέτει τα πεδία αυτά και επιπλέον το πεδίο //origin//.
 +  * Βασικό χαρακτηριστικό της κληρονομικότητας είναι ότι η κληρονομούμενη κλάση έχει όλα τα  χαρακτηριστικά της γονικής κλάσης. Υπό αυτή την έννοια, τα αντικείμενα της κλάσης //Rectangle// είναι και του τύπου //BasicRectangle//.
 +  * Παρατηρήστε τη δεσμευμένη λέξη //super// ως πρώτη εντολή του κάθε κατασκευαστή. Μέσω της κλήσης //super// καλείται ο κατασκευαστής της γονικής κλάσης.
 +</WRAP>
 ===== Προσβασιμότητα των κληρονομούμενων πεδίων ===== ===== Προσβασιμότητα των κληρονομούμενων πεδίων =====
  
 Μία κλάση η οποία κληρονομεί μία άλλη κλάση έχει πρόσβαση στα μέλη (πεδία και μεθόδους) της κλάσης αυτής ως εξής: Μία κλάση η οποία κληρονομεί μία άλλη κλάση έχει πρόσβαση στα μέλη (πεδία και μεθόδους) της κλάσης αυτής ως εξής:
  
-  * Έχει πρόσβαση στα //public// και //protected// μέλη της γονικής κλάσης +  * Έχει πρόσβαση στα //**public**// και //**protected**// μέλη της γονικής κλάσης 
-  * Έχει πρόσβαση στα //package private// μέλη (δηλ. τα μέλη χωρίς προσδιοριστή πρόσβασης) μόνο αν βρίσκεται στο ίδιο πακέτο με την γονική κλάση. +  * Έχει πρόσβαση στα //**package private**// μέλη (δηλ. τα μέλη χωρίς προσδιοριστή πρόσβασης) μόνο αν βρίσκεται στο ίδιο πακέτο με την γονική κλάση. 
-  * Δεν έχει πρόσβαση στα //private// μέλη της κλάσης. +  * Δεν έχει πρόσβαση στα //**private**// μέλη της κλάσης. 
  
-Αν υπάρχουν //public// μέθοδοι οι οποίες επιτρέπουν την πρόσβαση σε //private// πεδία, τότε αυτές μπορούν να χρησιμοποιηθούν για τον ορισμό ή για την λήψη της τιμής τους.+Αν υπάρχουν //**public**// μέθοδοι οι οποίες επιτρέπουν την πρόσβαση σε //**private**// πεδία, τότε αυτές μπορούν να χρησιμοποιηθούν για τον ορισμό ή για την λήψη της τιμής τους.
  
 ===== Τι μπορούμε να κάνουμε σε μία υποκλάση... ===== ===== Τι μπορούμε να κάνουμε σε μία υποκλάση... =====
Line 92: Line 137:
   * Να χρησιμοποιήσουμε τα πεδία της γονικής κλάσης στα οποία έχουμε πρόσβαση (public, protected, package-private στο ίδιο package).   * Να χρησιμοποιήσουμε τα πεδία της γονικής κλάσης στα οποία έχουμε πρόσβαση (public, protected, package-private στο ίδιο package).
   * Να ορίσουμε νέα πεδία.   * Να ορίσουμε νέα πεδία.
-  * Να ορίσουμε νέα πεδία που να έχουν ίδιο όνομα με πεδία της γονικής κλάσης. Σε αυτή την περίπτωση "κρύβουμε" τα πεδία της γονικής κλάσης. Η συγκεκριμένη πρακτική δεν συνίσταται και παραπέμπει σε λανθασμένη σχεδίαση κώδικα. 
   * Να χρησιμοποιήσουμε τις μεθόδους της γονικής κλάσης στις οποίες έχουμε πρόσβαση (public, protected, package-private στο ίδιο package).   * Να χρησιμοποιήσουμε τις μεθόδους της γονικής κλάσης στις οποίες έχουμε πρόσβαση (public, protected, package-private στο ίδιο package).
   * Μπορούμε να γράψουμε νέες στατικές ή μη στατικές μεθόδους για τη υποκλάση.   * Μπορούμε να γράψουμε νέες στατικές ή μη στατικές μεθόδους για τη υποκλάση.
Line 99: Line 143:
   * Μπορούμε να γράψουμε κατασκευαστές της υποκλάσης που χρησιμοποιούν κατασκευαστές της γονικής κλάσης.   * Μπορούμε να γράψουμε κατασκευαστές της υποκλάσης που χρησιμοποιούν κατασκευαστές της γονικής κλάσης.
  
-===== 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:access_modifiers Περιοριστές Πρόσβασης ]] | Επόμενο: [[ :java:type_casting Ρητές (explicit) και άρρητες (implicit) μετατροπές τύπων ]]|
  
java/inheritance.txt · Last modified: 2022/03/11 05:21 by gthanos