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

Τρόπος χρήσης των αθόρυβων τύπων στην Java

Ο κώδικας Java που χρησιμοποιεί παραδοσιακούς αριθμημένους τύπους είναι προβληματικός. Το Java 5 μάς έδωσε μια καλύτερη εναλλακτική μορφή με τη μορφή αθόρυβων τύπων. Σε αυτό το άρθρο, σας παρουσιάζω απαριθμημένους τύπους και τύπους χρηματοοικονομικών τύπων, σας δείχνω πώς να δηλώσετε ένα τυπικό enum και να το χρησιμοποιήσετε σε μια δήλωση διακόπτη και να συζητήσω για την προσαρμογή ενός τυπικού enum προσθέτοντας δεδομένα και συμπεριφορές. Τελειώσαμε το άρθρο εξερευνώντας το java.lang.Enum τάξη.

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

Από απαριθμημένους τύπους έως αθόρυβους τύπους

Ενα απαριθμημένος τύπος καθορίζει ένα σύνολο σχετικών σταθερών ως τιμές. Στα παραδείγματα περιλαμβάνονται μια εβδομάδα ημερών, οι τυπικές κατευθύνσεις πυξίδας Βορρά / Νότου / Ανατολής / Δύσης, οι ονομαστικές αξίες νομισμάτων και οι τύποι συμβολικών αναλυτών.

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

στατικό τελικό int DIR_NORTH = 0; στατικό τελικό int DIR_WEST = 1; στατικό τελικό int DIR_EAST = 2; στατικό τελικό int DIR_SOUTH = 3;

Υπάρχουν πολλά προβλήματα με αυτήν την προσέγγιση:

  • Έλλειψη ασφάλειας τύπου: Επειδή μια σταθερά απαριθμημένου τύπου είναι απλώς ακέραιος, οποιοσδήποτε ακέραιος μπορεί να καθοριστεί όπου απαιτείται η σταθερά. Επιπλέον, η προσθήκη, η αφαίρεση και άλλες μαθηματικές λειτουργίες μπορούν να πραγματοποιηθούν σε αυτές τις σταθερές. για παράδειγμα, (DIR_NORTH + DIR_EAST) / DIR_SOUTH), που δεν έχει νόημα.
  • Δεν υπάρχει χώρος ονομάτων: Οι σταθερές ενός αριθμημένου τύπου πρέπει να είναι προθεματισμένοι με κάποιο είδος (ελπίζουμε) μοναδικού αναγνωριστικού (π.χ. DIR_) για την αποφυγή συγκρούσεων με σταθερές άλλου αριθμημένου τύπου.
  • Εύθραυστο: Επειδή οι αριθμητικές σταθερές τύπων συγκεντρώνονται σε αρχεία κλάσης όπου αποθηκεύονται οι κυριολεκτικές τους τιμές (σε σταθερές ομάδες), η αλλαγή της τιμής μιας σταθεράς απαιτεί αυτά τα αρχεία κλάσης και αυτά τα αρχεία κλάσης εφαρμογών που εξαρτώνται από αυτά να ξαναχτιστούν. Διαφορετικά, θα παρουσιαστεί απροσδιόριστη συμπεριφορά κατά το χρόνο εκτέλεσης.
  • Ελλειψη πληροφόρησης: Όταν εκτυπώνεται μια σταθερά, η ακέραια τιμή εξέρχεται. Αυτή η έξοδος δεν σας λέει τίποτα για το τι αντιπροσωπεύει η ακέραια τιμή. Δεν προσδιορίζει καν τον αριθμημένο τύπο στον οποίο ανήκει η σταθερά.

Μπορείτε να αποφύγετε τα προβλήματα «έλλειψης ασφάλειας τύπου» και «έλλειψης πληροφοριών» χρησιμοποιώντας java.lang.String σταθερές. Για παράδειγμα, μπορείτε να καθορίσετε στατική τελική συμβολοσειρά DIR_NORTH = "NORTH";. Αν και η σταθερή τιμή είναι πιο σημαντική, Σειρά- οι σταθερές με βάση εξακολουθούν να υποφέρουν από "χώρο ονομάτων δεν υπάρχει" και προβλήματα ευθραυστότητας. Επίσης, σε αντίθεση με τις ακέραιες συγκρίσεις, δεν μπορείτε να συγκρίνετε τιμές συμβολοσειράς με το == και != τελεστές (οι οποίοι συγκρίνουν μόνο τις αναφορές).

Αυτά τα προβλήματα προκάλεσαν τους προγραμματιστές να εφεύρουν μια εναλλακτική λύση που βασίζεται στην τάξη, γνωστή ως Typesafe Enum. Αυτό το μοτίβο έχει περιγραφεί και κριθεί ευρέως. Ο Joshua Bloch παρουσίασε το μοτίβο στο στοιχείο 21 του Αποτελεσματικός οδηγός γλώσσας προγραμματισμού Java (Addison-Wesley, 2001) και σημείωσε ότι έχει κάποια προβλήματα. δηλαδή ότι είναι αμήχανο να συγκεντρωθούν οι σταθερές τυποποιημένες enum σε σύνολα και ότι οι σταθερές απαρίθμησης δεν μπορούν να χρησιμοποιηθούν σε διακόπτης δηλώσεις.

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

δημόσια τελική τάξη Suit // Δεν πρέπει να είναι δυνατή η υποκατηγορία Suit. {public static final Suit CLUBS = νέο κοστούμι (); δημόσιο στατικό τελικό κοστούμι DIAMONDS = νέο κοστούμι (); δημόσιο στατικό τελικό κοστούμι HEARTS = νέο κοστούμι (); δημόσιο στατικό τελικό κοστούμι SPADES = νέο κοστούμι (); ιδιωτικό κοστούμι () {} // Δεν πρέπει να μπορείτε να εισάγετε επιπλέον σταθερές. }

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

Κοστούμι = κοστούμι.DIAMONDS;

Ίσως θέλετε να ανακρίνετε κοστούμι σε ένα διακόπτης δήλωση όπως αυτή:

διακόπτης (suit) {case Suit.CLUBS: System.out.println ("clubs"); Διακοπή; case Suit.DIAMONDS: System.out.println ("διαμάντια"); Διακοπή; case Suit.HEARTS: System.out.println ("καρδιές"); Διακοπή; case Suit.SPADES: System.out.println ("μπαστούνια"); }

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

διακόπτης (κοστούμι) {case CLUBS: System.out.println ("clubs"); Διακοπή; υπόθεση DIAMONDS: System.out.println ("διαμάντια"); Διακοπή; HEARTS case: System.out.println ("καρδιές"); Διακοπή; case SPADES: System.out.println ("μπαστούνια"); }

Ωστόσο, όταν συναντά ο μεταγλωττιστής CLUBS, θα αναφέρει ένα σφάλμα που δηλώνει ότι δεν μπόρεσε να βρει το σύμβολο. Και ακόμα κι αν το βάλατε Κοστούμι Σε ένα πακέτο, εισήγαγε το πακέτο και στατικά εισήγαγε αυτές τις σταθερές, ο μεταγλωττιστής θα παραπονιόταν ότι δεν μπορούσε να μετατρέψει Κοστούμι προς την int όταν συναντά κοστούμι σε διακόπτης (κοστούμι). Όσον αφορά το καθένα υπόθεση, ο μεταγλωττιστής θα ανέφερε επίσης ότι απαιτείται μια σταθερή έκφραση.

Η Java δεν υποστηρίζει το μοτίβο Typesafe Enum διακόπτης δηλώσεις. Ωστόσο, εισήγαγε το typafe enum Γλωσσική λειτουργία για την ενσωμάτωση των πλεονεκτημάτων του μοτίβου κατά την επίλυση των προβλημάτων του και αυτή η λειτουργία υποστηρίζει διακόπτης.

Δήλωση ενός τυποποιημένου enum και χρήση του σε μια δήλωση διακόπτη

Μια απλή δήλωση enum τύπουafe στον κώδικα Java μοιάζει με τις αντίστοιχες γλώσσες στις γλώσσες C, C ++ και C #:

enum Κατεύθυνση {ΒΟΡΕΙΑ, ΔΥΤΙΚΗ, ΑΝΑΤΟΛΗ, ΝΟΤΙΑ}

Αυτή η δήλωση χρησιμοποιεί τη λέξη-κλειδί απαρίθμηση να εισαγάγει Κατεύθυνση ως τύπος enafe (ένα ειδικό είδος κλάσης), στο οποίο μπορούν να προστεθούν αυθαίρετες μέθοδοι και μπορούν να εφαρμοστούν αυθαίρετες διεπαφές. ο ΒΟΡΕΙΟΣ, ΔΥΤΙΚΑ, ΑΝΑΤΟΛΗ, και ΝΟΤΟΣσταθερές enum εφαρμόζονται ως σώματα κλάσης σταθερών ειδικών που ορίζουν ανώνυμες τάξεις που επεκτείνουν το περίβλημα Κατεύθυνση τάξη.

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

Η Λίστα 1 δηλώνει το προαναφερθέν enum και το χρησιμοποιεί στο a διακόπτης δήλωση. Δείχνει επίσης πώς να συγκρίνετε δύο σταθερές enum, για να προσδιορίσετε ποια σταθερά έρχεται πριν από την άλλη σταθερά.

Λίστα 1: TEDemo.java (έκδοση 1)

δημόσια τάξη TEDemo {enum Direction {NORTH, WEST, EAST, SOUTH} public static void main (String [] args) {for (int i = 0; i <Direction.values ​​(). length; i ++) {Direction d = Direction .values ​​() [i]; System.out.println (δ); διακόπτης (d) {case NORTH: System.out.println ("Move north"); Διακοπή; υπόθεση WEST: System.out.println ("Move west"); Διακοπή; υπόθεση EAST: System.out.println ("Μετακίνηση ανατολικά"); Διακοπή; υπόθεση SOUTH: System.out.println ("Move south"); Διακοπή; προεπιλογή: assert false: "άγνωστη κατεύθυνση"; }} System.out.println (Direction.NORTH.compareTo (Direction.SOUTH)); }}

Η λίστα 1 δηλώνει το Κατεύθυνση typafe enum και επαναλαμβάνει τα σταθερά μέλη του, τα οποία αξίες() επιστρέφει. Για κάθε τιμή, το διακόπτης Η δήλωση (βελτιωμένη για την υποστήριξη τύπων αθόρυβων) επιλέγει το υπόθεση που αντιστοιχεί στην τιμή τουρε και εξάγει ένα κατάλληλο μήνυμα. (Δεν προθέτετε μια σταθερά enum, π.χ. ΒΟΡΕΙΟΣ, με τον τύπο enum.) Τέλος, η Λίστα 1 αξιολογεί Direction.NORTH.compareTo (Direction.SOUTH) για να προσδιορίσετε εάν ΒΟΡΕΙΟΣ έρχεται πριν ΝΟΤΟΣ.

Μεταγλωττίστε τον πηγαίο κώδικα ως εξής:

javac TEDemo.java

Εκτελέστε την μεταγλωττισμένη εφαρμογή ως εξής:

java TEDemo

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

ΒΟΡΕΙΑ Μετακίνηση βόρεια ΔΥΤΙΚΑ Μετακίνηση δυτικά ΑΝΑΤΟΛΙΚΑ Μετακίνηση ανατολικά ΝΟΤΙΑ Μετακίνηση νότια -3

Η έξοδος αποκαλύπτει ότι η κληρονομική toString () Η μέθοδος επιστρέφει το όνομα της σταθεράς enum, και αυτό ΒΟΡΕΙΟΣ έρχεται πριν ΝΟΤΟΣ σε σύγκριση αυτών των σταθερών enum.

Προσθήκη δεδομένων και συμπεριφορών σε ένα τυπικό enum

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

Λίστα 2: TEDemo.java (έκδοση 2)

enum Coin {NICKEL (5), // οι σταθερές πρέπει να εμφανίζονται πρώτα DIME (10), QUARTER (25), DOLLAR (100); // το ερωτηματικό απαιτείται ιδιωτικό τελικό int valueInPennies; Νόμισμα (int valueInPennies) {this.valueInPennies = valueInPennies; } int toCoins (int pennies) {επιστροφή χρημάτων / valueInPennies; }} δημόσια τάξη TEDemo {public static void main (String [] args) {if (args.length! = 1) {System.err.println ("use: java TEDemo numberInPennies"); ΕΠΙΣΤΡΟΦΗ; } int pennies = Integer.parseInt (args [0]); για (int i = 0; i <Coin.values ​​(). length; i ++) System.out.println (pennies + "pennies mengandung" + Coin.values ​​() [i] .toCoins (pennies) + "" + Coin .values ​​() [i] .toString (). toLowerCase () + "s"); }}

Η λίστα 2 δηλώνει πρώτα α Νόμισμα απαρίθμηση. Μια λίστα παραμέτρων σταθερών προσδιορίζει τέσσερα είδη νομισμάτων. Το όρισμα που μεταβιβάστηκε σε κάθε σταθερά αντιπροσωπεύει τον αριθμό των πένων που αντιπροσωπεύει το νόμισμα.

Το όρισμα που μεταδίδεται σε κάθε σταθερά μεταδίδεται στην Νόμισμα (int valueInPennies) κατασκευαστή, το οποίο αποθηκεύει το όρισμα στο τιμέςInPennies πεδίο παρουσίας. Αυτή η μεταβλητή είναι προσβάσιμη από το toCoins () μέθοδος παρουσίας. Χωρίζεται στον αριθμό των πενών που μεταφέρθηκαν να πλάσει()'μικρό πέννες παράμετρος, και αυτή η μέθοδος επιστρέφει το αποτέλεσμα, το οποίο τυχαίνει να είναι ο αριθμός κερμάτων στη νομισματική ονομασία που περιγράφεται από το Νόμισμα συνεχής.

Σε αυτό το σημείο, ανακαλύψατε ότι μπορείτε να δηλώσετε πεδία παρουσίας, κατασκευαστές και μεθόδους παρουσίας σε ένα τυπικό enum. Σε τελική ανάλυση, ένα είδοςafe enum είναι ουσιαστικά ένα ειδικό είδος κλάσης Java.

ο TEDemo της τάξης κύριος() Η μέθοδος επιβεβαιώνει πρώτα ότι έχει οριστεί ένα όρισμα γραμμής εντολών. Αυτό το όρισμα μετατρέπεται σε ακέραιο αριθμό καλώντας το java.lang.Integer της τάξης parseInt () μέθοδο, η οποία αναλύει την τιμή του ορίσματος συμβολοσειράς σε ακέραιο (ή ρίχνει μια εξαίρεση όταν ανιχνεύεται μη έγκυρη είσοδος) Θα έχω περισσότερα να πω Ακέραιος αριθμός και τα ξαδέλφια μαθήματα στο μέλλον Java 101 άρθρο.

Προχωρώντας μπροστά, κύριος() επαναλαμβάνεται ΝόμισμαΟι σταθερές. Επειδή αυτές οι σταθερές αποθηκεύονται σε ένα Νόμισμα[] πίνακας, κύριος() αξιολογεί Τιμές νομισμάτων (). Μήκος για να προσδιορίσετε το μήκος αυτού του πίνακα. Για κάθε επανάληψη του δείκτη βρόχου Εγώ, κύριος() αξιολογεί Τιμές νομισμάτων () [i] για πρόσβαση στο Νόμισμα συνεχής. Επικαλείται καθένα toCoins () και toString () σε αυτήν τη σταθερά, η οποία αποδεικνύει περαιτέρω ότι Νόμισμα είναι ένα ειδικό είδος τάξης.

Μεταγλωττίστε τον πηγαίο κώδικα ως εξής:

javac TEDemo.java

Εκτελέστε την μεταγλωττισμένη εφαρμογή ως εξής:

java TEDemo 198

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

198 πένες περιέχει 39 νικέλια 198 πένες περιέχουν 19 δεκάδες 198 πένες περιέχουν 7 τέταρτα 198 πένες περιέχει 1 δολάρια

Εξερεύνηση του Ενουμ τάξη

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

ΕνουμΗ λίστα παραμέτρων τυπικού τύπου φαίνεται άθλια, αλλά δεν είναι τόσο δύσκολο να κατανοηθεί. Για παράδειγμα, στο πλαίσιο του Το νόμισμα επεκτείνει το Enum, θα ερμηνεύατε αυτήν τη λίστα παραμέτρων τυπικού τύπου ως εξής:

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

Εξετάζω ΕνουμΗ τεκμηρίωση Java και θα ανακαλύψετε ότι αντικαθιστά java.lang.Object'μικρό κλώνος (), ισούται με (), Οριστικοποιώ(), hashCode (), και toString () μεθόδους. Εκτός από toString (), δηλώνονται όλες αυτές οι παράνομες μέθοδοι τελικός έτσι ώστε να μην μπορούν να παρακαμφθούν σε μια υποκατηγορία:

  • κλώνος () παρακάμπτεται για να αποτρέψει την κλωνοποίηση των σταθερών έτσι ώστε να μην υπάρχουν ποτέ περισσότερα από ένα αντίγραφα μιας σταθεράς · Διαφορετικά, οι σταθερές δεν μπορούσαν να συγκριθούν μέσω == και !=.
  • ισούται με () παρακάμπτεται για σύγκριση σταθερών μέσω των αναφορών τους. Σταθερές με τις ίδιες ταυτότητες (==) πρέπει να έχει το ίδιο περιεχόμενο (ισούται με ()), και διαφορετικές ταυτότητες υποδηλώνουν διαφορετικά περιεχόμενα.
  • Οριστικοποιώ() παρακάμπτεται για να διασφαλιστεί ότι οι σταθερές δεν μπορούν να οριστικοποιηθούν.
  • hashCode () παρακάμπτεται επειδή ισούται με () παρακάμπτεται.
  • toString () παρακάμπτεται για να επιστρέψετε το όνομα της σταθεράς.

Ενουμ παρέχει επίσης τις δικές του μεθόδους. Αυτές οι μέθοδοι περιλαμβάνουν το τελικόςσύγκρισηΤο () (Ενουμ εφαρμόζει το java.lang. Συγκρίσιμο διεπαφή), getDeclaringClass (), όνομα(), και τακτικός() μέθοδοι: