Προγραμματισμός

Κληρονομικότητα στην Java, Μέρος 1: Η λέξη-κλειδί επεκτείνει

Η Java υποστηρίζει επαναχρησιμοποίηση τάξης μέσω κληρονομιάς και σύνθεσης. Αυτό το σεμινάριο δύο μερών σας διδάσκει πώς να χρησιμοποιείτε κληρονομιά στα προγράμματα Java. Στο Μέρος 1 θα μάθετε πώς να χρησιμοποιείτε το εκτείνεται λέξη-κλειδί για να αντλήσει μια θυγατρική τάξη από μια τάξη γονέων, να επικαλεστεί τους κατασκευαστές και τις μεθόδους γονικής τάξης και να παρακάμψει μεθόδους Στο Μέρος 2 θα περιηγηθείτε java.lang.Object, που είναι το superclass της Java από το οποίο κληρονομεί κάθε άλλη τάξη.

Για να ολοκληρώσετε τη μάθησή σας σχετικά με την κληρονομιά, φροντίστε να ρίξετε μια ματιά στη συμβουλή Java που εξηγεί πότε να χρησιμοποιώ σύνθεση έναντι κληρονομιάς Θα μάθετε γιατί η σύνθεση είναι ένα σημαντικό συμπλήρωμα της κληρονομιάς και πώς να τη χρησιμοποιήσετε για να προφυλάξετε από ζητήματα με ενθυλάκωση στα προγράμματα Java.

λήψη Λήψη του κώδικα Λήψη του πηγαίου κώδικα για παράδειγμα εφαρμογές σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Jeff Friesen για το JavaWorld.

Κληρονομιά Java: Δύο παραδείγματα

Κληρονομία είναι μια κατασκευή προγραμματισμού που χρησιμοποιούν οι προγραμματιστές λογισμικού για να δημιουργήσουν είναι-σχέσεις μεταξύ κατηγοριών. Η κληρονομικότητα μας επιτρέπει να αντλήσουμε πιο συγκεκριμένες κατηγορίες από πιο γενικές. Η πιο συγκεκριμένη κατηγορία είναι ένα είδος της πιο γενικής κατηγορίας. Για παράδειγμα, ένας λογαριασμός ελέγχου είναι ένα είδος λογαριασμού στον οποίο μπορείτε να κάνετε καταθέσεις και αναλήψεις. Ομοίως, ένα φορτηγό είναι ένα είδος οχήματος που χρησιμοποιείται για τη μεταφορά μεγάλων αντικειμένων.

Η κληρονομικότητα μπορεί να μειωθεί σε πολλαπλά επίπεδα, οδηγώντας σε ολοένα και πιο συγκεκριμένες κατηγορίες. Για παράδειγμα, το Σχήμα 1 δείχνει το αυτοκίνητο και το φορτηγό που κληρονομούν από το όχημα. βαγόνι σταθμού που κληρονομεί από το αυτοκίνητο? και σκουπίδια που κληρονομούν από φορτηγό Τα βέλη δείχνουν από πιο συγκεκριμένες κατηγορίες "θυγατρικών" (κάτω προς τα κάτω) σε λιγότερο συγκεκριμένες κατηγορίες "γονέων" (πάνω προς τα πάνω).

Τζεφ Φρίσεν

Αυτό το παράδειγμα δείχνει ενιαία κληρονομιά στην οποία μια παιδική κατηγορία κληρονομεί κατάσταση και συμπεριφορές από μία άμεση γονική κατηγορία. Σε αντίθεση, πολλαπλή κληρονομιά επιτρέπει σε μια παιδική κατηγορία να κληρονομήσει κατάσταση και συμπεριφορές από δύο ή περισσότερες άμεσες γονικές κατηγορίες. Η ιεραρχία στο Σχήμα 2 απεικονίζει πολλαπλή κληρονομιά.

Τζεφ Φρίσεν

Οι κατηγορίες περιγράφονται ανά τάξεις. Η Java υποστηρίζει μια μόνο κληρονομιά μέσω επέκταση τάξης, στην οποία μια τάξη κληρονομεί άμεσα προσβάσιμα πεδία και μεθόδους από μια άλλη τάξη επεκτείνοντας αυτήν την κλάση. Ωστόσο, η Java δεν υποστηρίζει πολλαπλή κληρονομιά μέσω επέκτασης τάξης.

Κατά την προβολή μιας ιεραρχίας κληρονομιάς, μπορείτε εύκολα να εντοπίσετε πολλαπλή κληρονομιά με την παρουσία ενός διαμαντιού. Το Σχήμα 2 δείχνει αυτό το μοτίβο στο πλαίσιο του οχήματος, του χερσαίου οχήματος, του θαλάσσιου οχήματος και του αιωρούμενου σκάφους.

Η λέξη-κλειδί επεκτείνει

Η Java υποστηρίζει επέκταση κλάσης μέσω του εκτείνεται λέξη-κλειδί. Όταν είναι παρόντα, εκτείνεται καθορίζει μια σχέση γονέα-παιδιού μεταξύ δύο τάξεων. Παρακάτω χρησιμοποιώ εκτείνεται να δημιουργήσει μια σχέση μεταξύ τάξεων Οχημα και Αυτοκίνητοκαι μετά μεταξύ λογαριασμός και Αποταμιευτικός λογαριασμός:

Λίστα 1. Το εκτείνεται Η λέξη-κλειδί καθορίζει μια σχέση γονέα-παιδιού

class Vehicle {// member deklarations} class Car επεκτείνει το Όχημα {// κληρονομήσει προσβάσιμα μέλη από το Vehicle // παρέχει δικές του δηλώσεις μελών} class class {// member Δηλώσεις μελών} class SavingsAccount επεκτείνει λογαριασμό {// κληρονομεί προσβάσιμα μέλη από το λογαριασμό // παρέχει δηλώσεις ιδίων μελών}

ο εκτείνεται Η λέξη-κλειδί καθορίζεται μετά το όνομα της τάξης και πριν από ένα άλλο όνομα τάξης. Το όνομα της τάξης πριν εκτείνεται προσδιορίζει το παιδί και το όνομα της τάξης μετά εκτείνεται προσδιορίζει τον γονέα. Είναι αδύνατο να προσδιορίσετε πολλά ονόματα τάξεων μετά εκτείνεται επειδή η Java δεν υποστηρίζει πολλαπλή κληρονομιά που βασίζεται στην τάξη.

Αυτά τα παραδείγματα κωδικοποιούν είναι σχέσεις: Αυτοκίνητοείναι ένα ειδικευμένος Οχημα και Αποταμιευτικός λογαριασμόςείναι ένα ειδικευμένος λογαριασμός. Οχημα και λογαριασμός είναι γνωστά ως μαθήματα βάσης, τάξεις γονέων, ή σούπερ γυαλιά. Αυτοκίνητο και Αποταμιευτικός λογαριασμός είναι γνωστά ως παράγωγες τάξεις, τάξεις παιδιών, ή υποκατηγορίες.

Τελικά μαθήματα

Μπορείτε να δηλώσετε μια τάξη που δεν πρέπει να επεκταθεί. για παράδειγμα για λόγους ασφαλείας. Στην Java, χρησιμοποιούμε το τελικός λέξη-κλειδί για την αποτροπή επέκτασης ορισμένων τάξεων. Απλώς προθέστε μια κεφαλίδα κλάσης με τελικός, όπως λέμε Κωδικός πρόσβασης τελικής τάξης. Δεδομένης αυτής της δήλωσης, ο μεταγλωττιστής θα αναφέρει ένα σφάλμα εάν κάποιος προσπαθήσει να επεκτείνει Κωδικός πρόσβασης.

Τα παιδικά μαθήματα κληρονομούν προσβάσιμα πεδία και μεθόδους από τις τάξεις γονέων και άλλους προγόνους τους. Ωστόσο, δεν κληρονομούν ποτέ κατασκευαστές. Αντ 'αυτού, οι τάξεις των παιδιών δηλώνουν τους δικούς τους κατασκευαστές. Επιπλέον, μπορούν να δηλώσουν τα δικά τους πεδία και μεθόδους για να τα διαφοροποιήσουν από τους γονείς τους. Εξετάστε την καταχώριση 2.

Λίστα 2. Ένα λογαριασμός τάξη γονέα

λογαριασμός κλάσης {ιδιωτικό όνομα συμβολοσειράς; ιδιωτικό μεγάλο ποσό · Λογαριασμός (όνομα συμβολοσειράς, μεγάλο ποσό) {this.name = name; setAmount (ποσό); } άκυρη κατάθεση (μεγάλο ποσό) {this.amount + = ποσό; } String getName () {όνομα επιστροφής; } μακρύ getAmount () {ποσό επιστροφής; } void setAmount (μεγάλο ποσό) {this.amount = ποσό; }}

Η Λίστα 2 περιγράφει μια γενική κλάση τραπεζικού λογαριασμού που έχει όνομα και αρχικό ποσό, τα οποία και τα δύο ορίζονται στον κατασκευαστή. Επίσης, επιτρέπει στους χρήστες να κάνουν καταθέσεις. (Μπορείτε να κάνετε αναλήψεις καταθέτοντας αρνητικά χρηματικά ποσά, αλλά θα αγνοήσουμε αυτήν τη δυνατότητα.) Σημειώστε ότι το όνομα του λογαριασμού πρέπει να οριστεί κατά τη δημιουργία ενός λογαριασμού.

Αντιπροσώπευση νομισματικών αξιών

μέτρηση πενών. Μπορεί να προτιμάτε να χρησιμοποιήσετε ένα διπλό ή α φλοτέρ για την αποθήκευση νομισματικών αξιών, αλλά αυτό μπορεί να οδηγήσει σε ανακρίβειες. Για μια καλύτερη λύση, σκεφτείτε Μεγάλο Δεκαδικό, η οποία αποτελεί μέρος της τυπικής βιβλιοθήκης κατηγορίας Java.

Η λίστα 3 παρουσιάζει α Αποταμιευτικός λογαριασμός παιδική τάξη που επεκτείνει την λογαριασμός τάξη γονέα.

Λίστα 3. Α Αποταμιευτικός λογαριασμός παιδική τάξη επεκτείνει το λογαριασμός τάξη γονέα

Η κλάση SavingsAccount επεκτείνει το λογαριασμό {SavingsAccount (μεγάλο ποσό) {super ("αποταμίευση", ποσό); }}

ο Αποταμιευτικός λογαριασμός Η τάξη είναι ασήμαντη επειδή δεν χρειάζεται να δηλώσει πρόσθετα πεδία ή μεθόδους. Ωστόσο, δηλώνει έναν κατασκευαστή που αρχικοποιεί τα πεδία σε αυτό λογαριασμός σούπερ γυαλί. Η προετοιμασία συμβαίνει όταν λογαριασμόςΟ κατασκευαστής καλείται μέσω Java σούπερ λέξη-κλειδί, ακολουθούμενη από μια παρένθεση λίστα ορισμάτων.

Πότε και πού να καλέσετε σούπερ ()

Απλα οπως Αυτό() πρέπει να είναι το πρώτο στοιχείο ενός κατασκευαστή που καλεί άλλο κατασκευαστή στην ίδια τάξη, σούπερ() πρέπει να είναι το πρώτο στοιχείο ενός κατασκευαστή που καλεί έναν κατασκευαστή στο superclass του. Εάν παραβιάσετε αυτόν τον κανόνα, ο μεταγλωττιστής θα αναφέρει ένα σφάλμα. Ο μεταγλωττιστής θα αναφέρει επίσης ένα σφάλμα εάν εντοπίσει ένα σούπερ() κλήση σε μια μέθοδο? μόνο κλήση ποτέ σούπερ() σε έναν κατασκευαστή.

Η λίστα 4 επεκτείνεται περαιτέρω λογαριασμός με Έλεγχος λογαριασμού τάξη.

Λίστα 4. Α Έλεγχος λογαριασμού παιδική τάξη επεκτείνει το λογαριασμός τάξη γονέα

Η κλάση CheckingAccount επεκτείνει τον λογαριασμό {CheckingAccount (μεγάλο ποσό) {super ("έλεγχος", ποσό); } άκυρη ανάληψη (μεγάλο ποσό) {setAmount (getAmount () - ποσό); }}

Έλεγχος λογαριασμού είναι λίγο πιο ουσιαστικό από Αποταμιευτικός λογαριασμός γιατί δηλώνει α αποσύρω() μέθοδος. Παρατηρήστε τις κλήσεις αυτής της μεθόδου setAmount () και getAmount (), οι οποίες Έλεγχος λογαριασμού κληρονομεί από λογαριασμός. Δεν μπορείτε να αποκτήσετε απευθείας πρόσβαση στο ποσό πεδίο σε λογαριασμός επειδή αυτό το πεδίο έχει δηλωθεί ιδιωτικός (βλ. Λίστα 2).

super () και ο κατασκευαστής χωρίς ορίσματα

Αν σούπερ() δεν καθορίζεται σε κατασκευαστή υποκατηγορίας και εάν το superclass δεν δηλώνει a κανένα επιχείρημα κατασκευαστή, τότε ο μεταγλωττιστής θα αναφέρει ένα σφάλμα. Αυτό συμβαίνει επειδή ο κατασκευαστής της υποκατηγορίας πρέπει να καλέσει a κανένα επιχείρημα κατασκευαστής superclass όταν σούπερ() δεν είναι παρών.

Παράδειγμα ιεραρχίας τάξης

Έχω δημιουργήσει ένα Λογαριασμός Demo κλάση εφαρμογών που σας επιτρέπει να δοκιμάσετε το λογαριασμός ιεραρχία τάξης. Πρώτα ρίξτε μια ματιά Λογαριασμός DemoΟ πηγαίος κώδικας.

Λίστα 5. Λογαριασμός Demo δείχνει την ιεραρχία κλάσης λογαριασμού

class AccountDemo {public static void main (String [] args) {SavingsAccount sa = νέο SavingsAccount (10000); System.out.println ("όνομα λογαριασμού:" + sa.getName ()); System.out.println ("αρχικό ποσό:" + sa.getAmount ()); sa.deposit (5000); System.out.println ("νέο ποσό μετά την κατάθεση:" + sa.getAmount ()); CheckingAccount ca = νέο CheckingAccount (20000); System.out.println ("όνομα λογαριασμού:" + ca.getName ()); System.out.println ("αρχικό ποσό:" + ca.getAmount ()); ca.deposit (6000); System.out.println ("νέο ποσό μετά την κατάθεση:" + ca.getAmount ()); ca.withdraw (3000); System.out.println ("νέο ποσό μετά την ανάληψη:" + ca.getAmount ()); }}

ο κύριος() πρώτη μέθοδος στην Λίστα 5 δείχνει Αποταμιευτικός λογαριασμός, έπειτα Έλεγχος λογαριασμού. Υποθέτοντας Account.java, Εξοικονόμηση λογαριασμού. Java, Έλεγχος λογαριασμού. Java, και AccountDemo.java Τα αρχεία προέλευσης βρίσκονται στον ίδιο κατάλογο, εκτελέστε μία από τις ακόλουθες εντολές για να μεταγλωττίσετε όλα αυτά τα αρχεία προέλευσης:

javac AccountDemo.java javac * .java

Εκτελέστε την ακόλουθη εντολή για να εκτελέσετε την εφαρμογή:

java AccountDemo

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

όνομα λογαριασμού: αρχικό ποσό αποταμίευσης: 10000 νέο ποσό μετά την κατάθεση: 15000 όνομα λογαριασμού: έλεγχος αρχικού ποσού: 20000 νέο ποσό μετά την κατάθεση: 26000 νέο ποσό μετά την ανάληψη: 23000

Παράκαμψη μεθόδου (και υπερφόρτωση μεθόδου)

Μια υποκατηγορία μπορεί καταπατώ (αντικαταστήστε) μια κληρονομική μέθοδο έτσι ώστε η έκδοση της μεθόδου της υποκατηγορίας να καλείται αντ 'αυτού. Μια μέθοδος παράκαμψης πρέπει να καθορίζει το ίδιο όνομα, λίστα παραμέτρων και τύπο επιστροφής με τη μέθοδο που παρακάμπτεται. Για να δείξω, έχω δηλώσει ένα Τυπώνω() μέθοδος στο Οχημα παρακάτω τάξη.

Λίστα 6. Δήλωση α Τυπώνω() μέθοδος για παράκαμψη

Όχημα κατηγορίας {private String make; ιδιωτικό μοντέλο String; ιδιωτικό έτος int? Όχημα (String make, String model, int year) {this.make = make; this.model = μοντέλο; αυτό. έτος = έτος; } String getMake () {return make; } String getModel () {model επιστροφής; } int getYear () {έτος επιστροφής; } άκυρη εκτύπωση () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}

Στη συνέχεια, παρακάμπτω Τυπώνω() στο Φορτηγό τάξη.

Λίστα 7. Παράκαμψη Τυπώνω() σε ένα Φορτηγό υποδιαίρεση τάξεως

Η κατηγορία Truck επεκτείνει το όχημα {ιδιωτική διπλή χωρητικότητα. Truck (String make, String model, int year, double tonage) {super (μάρκα, μοντέλο, έτος); this.tonnage = χωρητικότητα; } διπλή getTonnage () {επιστροφή χωρητικότητας; } άκυρη εκτύπωση () {super.print (); System.out.println ("Χωρητικότητα:" + χωρητικότητα); }}

Φορτηγό'μικρό Τυπώνω() Η μέθοδος έχει το ίδιο όνομα, τύπο επιστροφής και λίστα παραμέτρων με Οχημα'μικρό Τυπώνω() μέθοδος. Σημειώστε επίσης ότι Φορτηγό'μικρό Τυπώνω() μέθοδος πρώτων κλήσεων Οχημα'μικρό Τυπώνω() μέθοδος με πρόθεμα σούπερ. στο όνομα της μεθόδου. Συχνά είναι καλή ιδέα να εκτελέσετε πρώτα τη λογική του superclass και στη συνέχεια να εκτελέσετε τη λογική της subclass.

Κλήση μεθόδων superclass από μεθόδους subclass

Για να καλέσετε μια μέθοδο superclass από την επικρατούσα μέθοδο subclass, προθέστε το όνομα της μεθόδου με την δεσμευμένη λέξη σούπερ και ο χειριστής πρόσβασης μέλους. Διαφορετικά, θα καταλήξετε αναδρομικά κάνοντας παράκαμψη της υποκατηγορίας μέθοδο. Σε ορισμένες περιπτώσεις μια υποκατηγορία θα καλύψει μηιδιωτικός πεδία superclass δηλώνοντας τα ίδια ονόματα πεδίων. Μπορείς να χρησιμοποιήσεις σούπερ και ο χειριστής πρόσβασης μέλους για πρόσβαση στο μηιδιωτικός πεδία σούπερ γυαλιού.

Για να ολοκληρώσω αυτό το παράδειγμα, έχω αποσπάσει ένα VehicleDemo της τάξης κύριος() μέθοδος:

Φορτηγό = νέο φορτηγό ("Ford", "F150", 2008, 0,5); System.out.println ("Make =" + truck.getMake ()); System.out.println ("Model =" + truck.getModel ()); System.out.println ("Year =" + truck.getYear ()); System.out.println ("Tonnage =" + truck.getTonnage ()); truck.print ();

Η τελική γραμμή, truck.print ();, κλήσεις φορτηγό'μικρό Τυπώνω() μέθοδος. Αυτή η μέθοδος καλεί πρώτα Οχημα'μικρό Τυπώνω() για την παραγωγή της μάρκας, του μοντέλου και του έτους του φορτηγού. τότε βγάζει τη χωρητικότητα του φορτηγού. Αυτό το τμήμα της εξόδου φαίνεται παρακάτω:

Μάρκα: Ford, Μοντέλο: F150, Έτος: 2008 Χωρητικότητα: 0,5

Χρησιμοποιήστε την παράκαμψη της μεθόδου final to block

Περιστασιακά μπορεί να χρειαστεί να δηλώσετε μια μέθοδο που δεν πρέπει να παρακαμφθεί, για ασφάλεια ή για άλλο λόγο. Μπορείτε να χρησιμοποιήσετε το τελικός λέξη-κλειδί για το σκοπό αυτό. Για να αποφύγετε την παράκαμψη, απλώς προθέστε μια κεφαλίδα μεθόδου με τελικός, όπως λέμε final String getMake (). Στη συνέχεια, ο μεταγλωττιστής θα αναφέρει ένα σφάλμα εάν κάποιος προσπαθήσει να παρακάμψει αυτήν τη μέθοδο σε μια υποκατηγορία.

Μέθοδος υπερφόρτωσης έναντι παράκαμψης

Ας υποθέσουμε ότι αντικαταστήσατε το Τυπώνω() μέθοδος στην Λίστα 7 με την παρακάτω:

άκυρη εκτύπωση (String owner) {System.out.print ("Owner:" + owner); super.print (); }

Το τροποποιημένο Φορτηγό η τάξη έχει τώρα δύο Τυπώνω() μεθόδους: η προηγούμενη ρητά δηλωμένη μέθοδος και η μέθοδος που κληρονομήθηκε από Οχημα. ο άκυρη εκτύπωση (String owner) η μέθοδος δεν παρακάμπτει Οχημα'μικρό Τυπώνω() μέθοδος. Αντ 'αυτού, αυτό υπερφορτώσεις το.

Μπορείτε να εντοπίσετε μια προσπάθεια υπερφόρτωσης αντί να παρακάμψετε μια μέθοδο κατά το χρόνο μεταγλώττισης προθέτοντας μια κεφαλίδα μεθόδου μιας υποκατηγορίας με το @Καταπατώ σχόλιο:

@Override void print (String owner) {System.out.print ("Owner:" + owner); super.print (); }

Καθορισμός @Καταπατώ λέει στον μεταγλωττιστή ότι η δεδομένη μέθοδος παρακάμπτει μια άλλη μέθοδο. Αν κάποιος προσπάθησε να υπερφορτώσει τη μέθοδο αντ 'αυτού, ο μεταγλωττιστής θα αναφέρει ένα σφάλμα. Χωρίς αυτόν τον σχολιασμό, ο μεταγλωττιστής δεν θα αναφέρει κάποιο σφάλμα επειδή η μέθοδος υπερφόρτωσης είναι νόμιμη.

Πότε να χρησιμοποιήσετε το @Override

Αναπτύξτε τη συνήθεια του προθέματος υπερισχύουσες μεθόδους @Καταπατώ. Αυτή η συνήθεια θα σας βοηθήσει να εντοπίσετε τα λάθη υπερφόρτωσης πολύ νωρίτερα.