Η κληρονομικότητα στην Java αναφέρεται στη δυνατότητα μιας κλάσης να κληρονομεί τα χαρακτηριστικά (μεταβλητές και μεθόδους) μιας άλλης κλάσης. Αυτό σημαίνει ότι μια κλάση μπορεί να αποκτήσει τις ιδιότητες και τη συμπεριφορά της γονικής της κλάσης.
Η κληρονομικότητα υλοποιείται στη Java μέσω της δήλωσης της λέξης-κλειδιού extends
στον ορισμό της κλάσης. Μια υποκλάση (ή παράγωγη κλάση) δηλώνεται με τη λέξη-κλειδί extends
και το όνομα της γονικής κλάσης. Έτσι, η υποκλάση κληρονομεί τα χαρακτηριστικά της γονικής κλάσης και μπορεί επίσης να προσθέσει τα δικά της μοναδικά χαρακτηριστικά.
Η κληρονομικότητα στην Java επιτρέπει τη δημιουργία ιεραρχικών δομών κλάσεων, όπου οι κλάσεις μπορούν να οργανωθούν σε μια δομή που αντανακλά τις σχέσεις “είναι ένα” (is-a). Αυτό παρέχει τη δυνατότητα επαναχρησιμοποίησης του κώδικα, καθώς οι υποκλάσεις μπορούν να επωφεληθούν από τα ήδη υπάρχοντα χαρακτηριστικά και να προσθέσουν νέες λειτουργίες και συμπεριφορές.
Στο παρακάτω παράδειγμα, η κλάση GPU (υποκλάση) κληρονομεί τα χαρακτηριστικά και τις μεθόδους από την κλάση Workstation (γονική κλάση):
class Workstation { protected String model = "Unknown"; // Το μοντέλο του workstation public void printModel() { System.out.println("Model: " + model); // Εκτύπωση του μοντέλου } } class GPU extends Workstation { private String gpuModel = "Unknown"; // Το μοντέλο της GPU public void printGPUModel() { System.out.println("GPU Model: " + gpuModel); // Εκτύπωση του μοντέλου της GPU } } public class Main { public static void main(String[] args) { GPU myGPU = new GPU(); myGPU.printModel(); // Κλήση της κληρονομημένης μεθόδου από τη γονική κλάση myGPU.printGPUModel(); // Κλήση της μεθόδου της υποκλάσης } }
Ο παραπάνω κώδικας ορίζει τρεις κλάσεις: Workstation
, GPU
, και Main
.
Η κλάση Workstation
έχει ένα προστατευμένο πεδίο με την μεταβλητή model
, το οποίο αρχικοποιείται με την τιμή “Unknown”. Η κλάση περιέχει μια μέθοδο με το όνομα printModel()
, η οποία εκτυπώνει την τιμή του πεδίου model
.
Η κλάση GPU
κληρονομεί από την κλάση Workstation
και διαθέτει ένα ιδιωτικό πεδίο με την μεταβλητή gpuModel
, το οποίο αρχικοποιείται επίσης με την τιμή “Unknown”. Η κλάση περιέχει μια μέθοδο με το όνομα printGPUModel()
, η οποία εκτυπώνει την τιμή του πεδίου gpuModel
.
Η κλάση Main
περιέχει τη μέθοδο main
, η οποία είναι η εκκίνηση του προγράμματος. Μέσα σε αυτήν τη μέθοδο, δημιουργείται ένα αντικείμενο της κλάσης GPU
με το όνομα myGPU
. Στη συνέχεια, καλούνται οι μέθοδοι printModel()
και printGPUModel()
για να εκτυπωθούν τα μοντέλα του myGPU
.
Έτσι, ο κώδικας εκτυπώνει στην οθόνη το μοντέλο του myGPU
, που παίρνει από την κλάση Workstation
, καθώς και το μοντέλο της GPU, που ορίζεται στην κλάση GPU
.
Η κληρονομικότητα επιτρέπει σε μια υποκλάση να αποκτήσει τα χαρακτηριστικά και τις μεθόδους της γονικής κλάσης και, ταυτόχρονα, να προσθέσει τα δικά της χαρακτηριστικά και μεθόδους. Με αυτόν τον τρόπο, μπορούμε να αποφύγουμε την επανάληψη κώδικα και να δημιουργήσουμε έναν πιο οργανωμένο και ευανάγνωστο κώδικα.
Παρακάτω παρουσιάζουμε ένα ακόμα παράδειγμα κληρονομικότητας, όπου η κλάση Car (υποκλάση) κληρονομεί τα χαρακτηριστικά και τις μεθόδους από την κλάση Vehicle (γονική κλάση):
class Vehicle { protected String brand = "Unknown"; // Η μάρκα του οχήματος public void honk() { System.out.println("Tuut, tuut!"); // Εκτύπωση ήχου κόρνας } } class Car extends Vehicle { private String model = "Unknown"; // Το μοντέλο του αυτοκινήτου public void setModel(String model) { this.model = model; // Ορισμός του μοντέλου του αυτοκινήτου } public void printModel() { System.out.println("Model: " + model); // Εκτύπωση του μοντέλου του αυτοκινήτου } } public class Main { public static void main(String[] args) { Car myCar = new Car(); // Δημιουργία ενός αντικειμένου αυτοκινήτου myCar.setBrand("Ford"); // Κλήση κληρονομημένης μεθόδου από τη γονική κλάση για ορισμό της μάρκας myCar.setModel("Mustang"); // Κλήση μεθόδου της υποκλάσης για ορισμό του μοντέλου System.out.println("Brand: " + myCar.brand); // Εκτύπωση της μάρκας (κληρονομημένο χαρακτηριστικό) myCar.printModel(); // Κλήση μεθόδου της υποκλάσης για εκτύπωση του μοντέλου myCar.honk(); // Κλήση κληρονομημένης μεθόδου από τη γονική κλάση για τον ήχο της κόρνας } }
Ο κώδικας δημιουργεί δύο κλάσεις, την κλάση “Vehicle” και την κλάση “Car”. Η κλάση “Car” κληρονομεί την κλάση “Vehicle”, προσθέτοντας ένα επιπλέον χαρακτηριστικό “model” και δύο μεθόδους “setModel” και “printModel”. Η κλάση “Vehicle” έχει ένα προστατευμένο χαρακτηριστικό “brand” και μια μέθοδο “honk” που εκτυπώνει έναν ήχο κόρνας.
Στην κύρια κλάση “Main” δημιουργείται ένα αντικείμενο τύπου “Car” με όνομα “myCar”. Στη συνέχεια, καλούνται μεθόδοι πάνω στο αντικείμενο “myCar”. Η μέθοδος “setBrand” καλείται για να ορίσει τη μάρκα του αυτοκινήτου, η μέθοδος “setModel” καλείται για να ορίσει το μοντέλο του αυτοκινήτου. Στη συνέχεια, εκτυπώνεται η μάρκα του αυτοκινήτου και το μοντέλο του αυτοκινήτου με τη χρήση της μεθόδου “printModel”. Τέλος, καλείται η μέθοδος “honk” για να εκτυπωθεί ο ήχος της κόρνας του αυτοκινήτου.
Συνολικά, ο κώδικας δημιουργεί ένα αντικείμενο αυτοκινήτου, ορίζει τη μάρκα και το μοντέλο του, και εκτυπώνει τις πληροφορίες αυτές στην οθόνη.
Το αποτέλεσμα που θα εμφανιστεί στην οθόνη είναι:
Brand: Ford Model: Mustang Tuut, tuut!
Αυτό συμβαίνει επειδή πρώτα ορίζεται η μάρκα “Ford” και το μοντέλο “Mustang” στο αντικείμενο “myCar”. Έπειτα, εκτυπώνεται η μάρκα και το μοντέλο με τη χρήση της μεθόδου “printModel”. Τέλος, καλείται η μέθοδος “honk”, η οποία εκτυπώνει τον ήχο “Tuut, tuut!” στην οθόνη.
Με τη χρήση της κληρονομικότητας, δημιουργούμε μια ιεραρχία κλάσεων που επιτρέπει την ανάπτυξη πιο εύκολα επεκτάσιμων και συντηρήσιμων εφαρμογών.
[adinserter block=”2″]
Αν δεν θέλετε άλλες κλάσεις να κληρονομήσουν από μια κλάση, χρησιμοποιήστε τη λέξη-κλειδί final:
final class MyClass { // κώδικας της κλάσης }
Στο παραπάνω παράδειγμα, η κλάση MyClass είναι δηλωμένη ως τελική (final) και δεν μπορεί να χρησιμοποιηθεί ως γονική κλάση για τη δημιουργία υποκλάσεων. Αν προσπαθήσετε να δημιουργήσετε μια υποκλάση της MyClass, θα λάβετε ένα σφάλμα κατά την ανάλυση του κώδικα της υποκλάσης, που υποδεικνύει ότι η MyClass δεν μπορεί να κληρονομηθεί.
Μπορούμε να δείξουμε ένα παράδειγμα σφάλματος όταν προσπαθούμε να κληρονομήσουμε μια τελική κλάση. Για παράδειγμα:
final class Vehicle { // κώδικας της κλάσης } class Car extends Vehicle { // κώδικας της κλάσης }
Στο παραπάνω παράδειγμα, η κλάση Vehicle
έχει δηλωθεί ως τελική με τη χρήση του final
. Καθώς η κλάση Car
προσπαθεί να κληρονομήσει από την Vehicle
, θα λάβετε ένα σφάλμα κατά τη μεταγλώττιση που θα σας ενημερώνει ότι η κλάση Vehicle
δεν μπορεί να κληρονομηθεί επειδή είναι τελική.
Το σφάλμα θα εμφανιστεί κατά τη μεταγλώττιση και θα αναφέρει κάτι παρόμοιο με το ακόλουθο μήνυμα:
cannot inherit from final Vehicle class Car extends Vehicle { ^
Αυτό σημαίνει ότι η κλάση Car
δεν μπορεί να κληρονομήσει την Vehicle
, επειδή η Vehicle
δηλώθηκε ως τελική.