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

Δημιουργήστε απαριθμημένες σταθερές στην Java

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

Η χρήση απαριθμημένων σταθερών μπορεί να κάνει τον κώδικα πιο ευανάγνωστο. Για παράδειγμα, ίσως θέλετε να ορίσετε έναν νέο τύπο δεδομένων με το όνομα Χρώμα με σταθερές ΚΟΚΚΙΝΟ, ΠΡΑΣΙΝΟ και ΜΠΛΕ ως πιθανές τιμές. Η ιδέα είναι να έχετε το Χρώμα ως χαρακτηριστικό άλλων αντικειμένων που δημιουργείτε, όπως αντικείμενα αυτοκινήτου:

 αυτοκίνητο κατηγορίας {Χρώμα χρώματος; ...} 

Στη συνέχεια, μπορείτε να γράψετε σαφή, αναγνώσιμο κώδικα, όπως αυτό:

 myCar.color = ΚΟΚΚΙΝΟ; 

αντί για κάτι σαν:

 myCar.color = 3; 

Ένα ακόμη πιο σημαντικό χαρακτηριστικό των απαριθμημένων σταθερών σε γλώσσες όπως το Pascal είναι ότι είναι ασφαλείς για τον τύπο. Με άλλα λόγια, δεν είναι δυνατή η εκχώρηση μη έγκυρου χρώματος στο χαρακτηριστικό χρώματος - πρέπει πάντα να είναι ΚΟΚΚΙΝΟ, ΠΡΑΣΙΝΟ ή ΜΠΛΕ. Αντίθετα, εάν η μεταβλητή χρώματος ήταν int, τότε θα μπορούσατε να αντιστοιχίσετε οποιοδήποτε έγκυρο ακέραιο σε αυτήν, ακόμη και αν αυτός ο αριθμός δεν αντιπροσωπεύει ένα έγκυρο χρώμα.

Αυτό το άρθρο σάς παρέχει ένα πρότυπο για τη δημιουργία απαριθμημένων σταθερών που είναι:

  • Πληκτρολογήστε χρηματοκιβώτιο
  • Εκτυπώσιμος
  • Παραγγέλθηκε, για χρήση ως ευρετήριο
  • Συνδέεται, για βρόχο προς τα εμπρός ή προς τα πίσω
  • Αμέτρητος

Σε ένα μελλοντικό άρθρο, θα μάθετε πώς να επεκτείνετε τις αριθμημένες σταθερές για να εφαρμόσετε συμπεριφορά που εξαρτάται από την κατάσταση.

Γιατί να μην χρησιμοποιήσετε στατικούς τελικούς;

Ένας κοινός μηχανισμός για απαριθμημένες σταθερές χρησιμοποιεί στατικές τελικές μεταβλητές int, όπως αυτό:

 στατικό τελικό int RED = 0; στατικό τελικό int ΠΡΑΣΙΝΟ = 1; στατικό τελικό int BLUE = 2; ... 

Οι στατικοί τελικοί είναι χρήσιμοι

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

Για παράδειγμα, μπορείτε να γράψετε έναν βρόχο για να δημιουργήσετε μια λίστα με τα αγαπημένα χρώματα ενός πελάτη:

 για (int i = 0; ...) {if (customerLikesColor (i)) {favoriteColors.add (i); }} 

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

PiecePicture redPiece = νέο PiecePicture (ΚΟΚΚΙΝΟ); PiecePicture greenPiece = νέο PiecePicture (ΠΡΑΣΙΝΟ); PiecePicture bluePiece = νέο PiecePicture (ΜΠΛΕ);

void placePiece (int location, int color) {setPosition (τοποθεσία); if (χρώμα == ΚΟΚΚΙΝΟ) {display (redPiece); } αλλιώς εάν (color == GREEN) {display (greenPiece); } αλλιώς {display (bluePiece); }}

Ωστόσο, χρησιμοποιώντας τις ακέραιες τιμές για ευρετήριο σε μια σειρά κομματιών, μπορείτε να απλοποιήσετε τον κώδικα για:

 PiecePicture [] piece = {new PiecePicture (ΚΟΚΚΙΝΟ), νέο PiecePicture (ΠΡΑΣΙΝΟ), νέο PiecePicture (ΜΠΛΕ)}; void placePiece (int location, int color) {setPosition (τοποθεσία); οθόνη (κομμάτι [χρώμα]); } 

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

Αλλά οι στατικοί τελικοί είναι επικίνδυνοι

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

Για παράδειγμα, ο βρόχος προτίμησης χρώματος μπορεί να έχει ως εξής:

 για (int i = 0; i <= BLUE; i ++) {if (customerLikesColor (i)) {favoritColors.add (i); }} 

Αργότερα, μπορείτε να προσθέσετε ένα νέο χρώμα:

 στατικό τελικό int RED = 0; στατικό τελικό int ΠΡΑΣΙΝΟ = 1; στατικό τελικό int BLUE = 2; στατικό τελικό int MAGENTA = 3; 

Ή μπορείτε να καταργήσετε ένα:

 στατικό τελικό int RED = 0; στατικό τελικό int BLUE = 1; 

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

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

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

 στατικό τελικό String RED = "red" .intern (); ... 

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

Τύπος ασφάλειας

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

Μια κομψή λύση δόθηκε στο άρθρο του Philip Bishop στο JavaWorld, "Typesafe σταθερές σε C ++ και Java."

Η ιδέα είναι πραγματικά απλή (μόλις τη δείτε!):

δημόσια τελική τάξη Χρώμα {// τελική τάξη !! ιδιωτικό χρώμα () {} // ιδιωτικός κατασκευαστής !!

δημόσιο στατικό τελικό Χρώμα ΚΟΚΚΙΝΟ = νέο χρώμα (); δημόσιο στατικό τελικό Χρώμα ΠΡΑΣΙΝΟ = νέο χρώμα (); δημόσιο στατικό τελικό Χρώμα ΜΠΛΕ = νέο Χρώμα (); }

Επειδή η τάξη ορίζεται ως τελική, δεν μπορεί να υποκατηγορηθεί. Δεν θα δημιουργηθούν άλλες τάξεις από αυτό. Επειδή ο κατασκευαστής είναι ιδιωτικός, άλλες μέθοδοι δεν μπορούν να χρησιμοποιήσουν την κλάση για να δημιουργήσουν νέα αντικείμενα. Τα μόνα αντικείμενα που θα δημιουργηθούν ποτέ με αυτήν την τάξη είναι τα στατικά αντικείμενα που δημιουργεί η τάξη από μόνη της την πρώτη φορά που γίνεται αναφορά στην τάξη! Αυτή η εφαρμογή είναι μια παραλλαγή του μοτίβου Singleton που περιορίζει την κλάση σε έναν προκαθορισμένο αριθμό παρουσιών. Μπορείτε να χρησιμοποιήσετε αυτό το μοτίβο για να δημιουργήσετε ακριβώς μια τάξη κάθε φορά που χρειάζεστε ένα Singleton ή να το χρησιμοποιήσετε όπως φαίνεται εδώ για να δημιουργήσετε έναν καθορισμένο αριθμό παρουσιών. (Το μοτίβο Singleton ορίζεται στο βιβλίο Σχέδια σχεδίασης: Στοιχεία επαναχρησιμοποιήσιμου αντικειμενοστραφούς λογισμικού από τους Gamma, Helm, Johnson και Vlissides, Addison-Wesley, 1995. Ανατρέξτε στην ενότητα Πόροι για έναν σύνδεσμο για αυτό το βιβλίο.)

Το συναρπαστικό μέρος αυτού του ορισμού της κατηγορίας είναι ότι χρησιμοποιεί η τάξη εαυτό για να δημιουργήσετε νέα αντικείμενα. Την πρώτη φορά που αναφέρετε το ΚΟΚΚΙΝΟ, δεν υπάρχει. Αλλά η πράξη πρόσβασης στην τάξη στην οποία ορίζεται το RED προκαλεί τη δημιουργία της, μαζί με τις άλλες σταθερές. Βεβαίως, αυτό το είδος αναδρομικής αναφοράς είναι μάλλον δύσκολο να φανταστεί κανείς. Αλλά το πλεονέκτημα είναι η συνολική ασφάλεια τύπου. Μια μεταβλητή τύπου Color δεν μπορεί ποτέ να εκχωρηθεί τίποτα άλλο από τα ΚΟΚΚΙΝΟ, ΠΡΑΣΙΝΟ ή ΜΠΛΕ αντικείμενα που Χρώμα δημιουργεί τάξη.

Αναγνωριστικά

Η πρώτη βελτίωση της κατηγορίας σταθερής απαριθμημένης σταθεράς είναι η δημιουργία συμβολοσειράς αναπαράστασης των σταθερών. Θέλετε να μπορείτε να δημιουργήσετε μια αναγνώσιμη έκδοση της τιμής με μια γραμμή όπως αυτή:

 System.out.println (myColor); 

Κάθε φορά που εξάγετε ένα αντικείμενο σε μια ροή εξόδου χαρακτήρων όπως System.out, και κάθε φορά που συνδυάζετε ένα αντικείμενο σε μια συμβολοσειρά, η Java καλεί αυτόματα το toString () μέθοδο για αυτό το αντικείμενο. Αυτός είναι ένας καλός λόγος για να ορίσετε ένα toString () μέθοδο για κάθε νέα τάξη που δημιουργείτε.

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

Εδώ είναι μια τροποποίηση στο Χρώμα τάξη που παρέχει ένα χρήσιμο toString () μέθοδος:

δημόσια τελική τάξη Χρώμα { ιδιωτικό αναγνωριστικό συμβολοσειράς; ιδιωτικό χρώμα (Συμβολοσειρά anID) {this.id = anID; } public String toString () {return this.id; }

δημόσιο στατικό τελικό Χρώμα ΚΟΚΚΙΝΟ = νέο χρώμα (

"Το κόκκινο"

); δημόσιο στατικό τελικό ΧΡΩΜΑ = νέο χρώμα (

"Πράσινος"

); δημόσιο στατικό τελικό Χρώμα ΜΠΛΕ = νέο χρώμα (

"Μπλε"

); }

Αυτή η έκδοση προσθέτει μια ιδιωτική μεταβλητή String (id). Ο κατασκευαστής έχει τροποποιηθεί για να λάβει ένα όρισμα String και να το αποθηκεύσει ως αναγνωριστικό του αντικειμένου. ο toString () Στη συνέχεια επιστρέφει το αναγνωριστικό του αντικειμένου.

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

 textField1.setText ("" + myColor); 

Εάν δεν αγαπήσετε όλες τις παρενθέσεις στο Lisp, θα το βρείτε λίγο πιο ευανάγνωστο από το εναλλακτικό:

 textField1.setText (myColor.toString ()); 

Είναι επίσης πιο εύκολο να βεβαιωθείτε ότι έχετε τοποθετήσει τον σωστό αριθμό παρενθέσεων κλεισίματος!

Παραγγελία και ευρετηρίαση

Η επόμενη ερώτηση είναι πώς να κάνετε ευρετήριο σε ένα διάνυσμα ή έναν πίνακα χρησιμοποιώντας μέλη του

Χρώμα

τάξη. Ο μηχανισμός θα είναι να εκχωρήσει έναν κανονικό αριθμό σε κάθε σταθερά κλάσης και να τον αναφέρει χρησιμοποιώντας το χαρακτηριστικό

.ορδ

, σαν αυτό:

 void placePiece (int location, int color) {setPosition (τοποθεσία); οθόνη (κομμάτι [χρώμα.ορδ]); } 

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

Να πώς εκχωρούνται οι κανονικοί αριθμοί:

δημόσια τελική τάξη Χρώμα {ιδιωτικό αναγνωριστικό συμβολοσειράς; δημόσιο τελικό int?ιδιωτικό στατικό int άνωBound = 0; ιδιωτικό χρώμα (String anID) {this.id = anID; this.ord = upperBound ++; } δημόσια συμβολοσειρά toString () {return this.id; } δημόσιο στατικό int μέγεθος () {return upperBound; }

δημόσιο στατικό τελικό Χρώμα ΚΟΚΚΙΝΟ = νέο χρώμα ("Κόκκινο"); δημόσιο στατικό τελικό Χρώμα ΠΡΑΣΙΝΟ = νέο χρώμα ("Πράσινο"); δημόσιο στατικό τελικό Χρώμα ΜΠΛΕ = νέο Χρώμα ("Μπλε"); }

Αυτός ο κώδικας χρησιμοποιεί τον νέο ορισμό JDK έκδοσης 1.1 μιας μεταβλητής "κενό τελικό" - μια μεταβλητή στην οποία έχει εκχωρηθεί μια τιμή μία και μία μόνο φορά. Αυτός ο μηχανισμός επιτρέπει σε κάθε αντικείμενο να έχει τη δική του μη στατική τελική μεταβλητή, ord, η οποία θα εκχωρηθεί μία φορά κατά τη δημιουργία αντικειμένων και στη συνέχεια θα παραμείνει αμετάβλητη. Η στατική μεταβλητή άνω όριο παρακολουθεί το επόμενο αχρησιμοποίητο ευρετήριο στη συλλογή. Αυτή η τιμή γίνεται η ord χαρακτηριστικό όταν δημιουργείται το αντικείμενο, μετά το οποίο αυξάνεται το ανώτατο όριο.

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

Ένας καθαριστής μπορεί να αποφασίσει ότι η μεταβλητή ord θα πρέπει να είναι ιδιωτική και να ονομάζεται η μέθοδος ord () θα πρέπει να το επιστρέψει - αν όχι, μια μέθοδο που ονομάζεται getOrd (). Κλίνω προς την άμεση πρόσβαση στο χαρακτηριστικό, για δύο λόγους. Το πρώτο είναι ότι η έννοια ενός τακτικού είναι σαφώς αυτή του int. Υπάρχει μικρή πιθανότητα, εάν υπάρχει, ότι η εφαρμογή θα άλλαζε ποτέ. Ο δεύτερος λόγος είναι ότι αυτό που πραγματικά θέλω είναι η ικανότητα χρήσης του αντικειμένου σαν να ήταν int, όπως θα μπορούσατε σε μια γλώσσα όπως το Pascal. Για παράδειγμα, ίσως θέλετε να χρησιμοποιήσετε το χαρακτηριστικό χρώμα για ευρετηρίαση ενός πίνακα. Αλλά δεν μπορείτε να χρησιμοποιήσετε ένα αντικείμενο Java για να το κάνετε απευθείας. Αυτό που πραγματικά θα θέλατε να πείτε είναι:

 οθόνη (κομμάτι [χρώμα]); // επιθυμητό, ​​αλλά δεν λειτουργεί 

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

 οθόνη (κομμάτι [color.ord]); // πιο κοντά στο επιθυμητό 

αντί για τη μακρά εναλλακτική λύση:

 οθόνη (κομμάτι [color.ord ()]); // επιπλέον παρενθέσεις 

ή το πιο μακρύτερο:

 οθόνη (κομμάτι [color.getOrd ()]); // επιπλέον παρενθέσεις και κείμενο 

Η γλώσσα του Άιφελ χρησιμοποιεί την ίδια σύνταξη για πρόσβαση σε χαρακτηριστικά και μεθόδους επίκλησης. Αυτό θα ήταν το ιδανικό. Δεδομένης της αναγκαιότητας επιλογής ενός ή του άλλου, ωστόσο, έχω πάει με την πρόσβαση ord ως χαρακτηριστικό. Με οποιαδήποτε τύχη, το αναγνωριστικό ord θα γίνει τόσο οικεία ως αποτέλεσμα της επανάληψης που η χρήση της θα φαίνεται τόσο φυσική όσο η γραφή int. (Όσο φυσικό μπορεί να είναι.)

Βρόχος

Το επόμενο βήμα είναι η δυνατότητα επανάληψης των σταθερών τάξης. Θέλετε να μπορείτε να κάνετε βρόχο από την αρχή έως το τέλος:

 για (Color c = Color.first (); c! = null; c = c.next ()) {...} 

ή από το τέλος πίσω στην αρχή:

 για (Color c = Color.last (); c! = null; c = c.prev ()) {...} 

Αυτές οι τροποποιήσεις χρησιμοποιούν στατικές μεταβλητές για να παρακολουθούν το τελευταίο αντικείμενο που δημιουργήθηκε και να το συνδέσουν στο επόμενο αντικείμενο: