Δείτε αυτές τις συχνές ερωτήσεις για περισσότερες πληροφορίες:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#SECTION1
Ακολουθούν ορισμένα σημαντικά σημεία από αυτό το FAQ.
Παράδειγμα (γενικός τύπος):
κλάση Pair<X,Y> {
ιδιωτικό Χ πρώτα?
ιδιωτικο Υ δευτερο? δημόσιο ζεύγος(X a1, Y a2) {
πρώτο = a1;
δεύτερο = a2;
}
public X getFirst() { return first; }
public Y getSecond() { return second; }
δημόσιο κενό setFirst(X arg) { first = arg; }
public void setSecond(Y arg) { second = arg; }
}
Η τάξη Ζεύγος έχει δύο παραμέτρους τύπου Χ και Υ . Αντικαθίστανται από ορίσματα τύπου όταν ο γενικός τύπος Ζεύγος είναι στιγμιαίο. Για παράδειγμα, στη δήλωση Pair<String, Date> την παράμετρο τύπου Χ αντικαθίσταται από το όρισμα τύπου Σειρά και Υ αντικαθίσταται από Ημερομηνία .
Το εύρος των αναγνωριστικών Χ και Υ είναι ολόκληρος ο ορισμός της τάξης. Σε αυτό το πεδίο εφαρμογής οι δύο παράμετροι τύπου Χ και Υ χρησιμοποιούνται σαν να ελλάτη τυπός (με νεκένως σεματικός). Στο παραπάνω παράδειγμα, παράμετροι τυπός χρησιμοποιούνται ως όρισμα και τύπος περάτης περάτης παραστής και τύπος πεδίων παραστών.
Οι παράμετροι τυπου μπορούν να δηλωθούν με όρια. Τα όρια δίνουν πρόσβαση στις μεθόδους του τύπου που αντιπροσωπεύει η παράμετρος. Στην παραδειγμά μας, δεν επικαλούμαστε μεθόδους αγνώστου τύπου Χ και Υ . Για το λόγο αυτό, οι δύο παράμετροι του τύπου είναι απεριόριστες.
Παράδειγμα:
public void printPair( Pair<String,Long> ζευγάρι) {
System.out.println("("+pair.getFirst()+","+pair.getSecond()+")");
} Pair<String,Long> όριο = νέο Pair<String,Long> ("μέγιστο", 1024L);
printPair(limit);
Παράδειγμα (τύπου παραμετροποιημένου χαρακτήρα μπαλαντέρ):
public void printPair( Pair<;,?> ζευγάρι) {
System.out.println("("+pair.getFirst()+","+pair.getSecond()+")");
} Pair<;,?> όριο = νέο Pair<String,Long> ("μέγιστο", 1024L);
printPair(limit);
Όλα τα στιγμιότυπα ενός γενικού τύπου μοιράζονται την ίδια αναπαράσταση τύπου χρόνου εκτέλεσης, δηλαδή την αναπαράσταση του πρωτογενούς τύπου. Για παράδειγμα, οι παρουσίες ενός γενικού τύπου Λίστα , όπως π.χ List<Date> , List<String> , List<Long> , κ.λπ. έχουν διαφορετικούς στατικούς τύπους κατά το χρόνο μεταγλώττισης, αλλά τον ίδιο δυναμικό τύπο Λίστα κατά το χρόνο.
Ένα καστ αποτελείται από δύο μέρη:
- ένας έλεγχος στατικού τύπου που εκτελείται από τον μεταγλωττιστή κατά το χρόνο μεταγλώττισης και
- ένας δυναμικός έλεγχος τύπου που εκτελείται από την εικονική μηχανή κατά το χρόνο εκτέλεσης.
Το στατικό μέρος ξεχωρίζει ανόητα καστ, που δεν μπορούν να πετύχουν, όπως το καστ από Σειρά ότι Ημερομηνία ή από List<String> ότι List<Date> .
Το δυναμικό μέρος χρησιμοποιεί τις πληροφορίες του τύπου του χρόνου εκτέλεσης και εκτελεί τον έλεγχο του τύπου έναντι του χρόνου εκτέλεσης. γερηγει α ClassCastException εάν ο δυναμικός τύπος του αντικειμένου δεν είναι ο τύπος στόχου (ή ένας υποτύπος του τύπου στόχου) του καστ. Παραδείγματα καστ με δυναμικό μέρος είναι το καστ από Αντικείμενο ότι Σειρά ή από Αντικείμενο ότι List<String> . Αυτά είναι τα λεγόμενα downcasts, από το ενον υπερτύπο σε ενον υποτύπο.
Παραδείγματα παραμετροποιημένων τυπος σκυροδέματος είναι List<String> , Map<String,Date> , αλλά όχι List<; επεκτείνει τον Αριθμό> ή Map<String,?> .
Μερικές φορές αναμένεται ότι α Λίστα<Object> θα ήταν ένας υπερτύπος του α List<String> , γιατί Αντικείμενο είναι ένας υπερτύπος του Σειρά . Αυτή η προσδοκία πηγάζει από το γεγονός ότι υπάρχει ένας τέτοιος τύπος σχέσης για πίνακες: Αντικείμενο[] είναι ένας υπερτύπος του Σειρά[] , γιατί Αντικείμενο είναι ένας υπερτύπος του Σειρά . (Αυτός ο τύπος σχέσης είναι γνωστός ως συνδιακύμανση .) Η σχέση υπερ-υποτύπου των τύπων συστατικών επεκτείνεται στους αντίστοιχους τύπους πίνακα. Δεν υπάρχει τέτοια σχέση τύπου για στιγμιότυπα γενικών τύπων. (Οι παραμετροποιημένοι τύποι είναι δεν συμπαράλλαχ.)
Η έλλειψη σχέσης υπερ-υποτύπου μεταξύ περιπτώσεων του ίδιου γενικού τύπου έχει αρκετές συνέπειες. Εδώ είναι ένα παράδειγμα.
Παράδειγμα:
void printAll(ArrayList<Object> c) {
για (Αντικείμενο ο : γ)
System.out.println(o);
} Λίστα ArrayList<String> = νέα ArrayList<String>();
... συμπλήρωση λίστας ...
printAll(list); // σφάλμα
ΕΝΑ ArrayList<String> Το αντικείμενο δεν μπορεί να μεταβιβαστεί ως όρισμα σε μια μέθοδο που ζητά a ArrayList<Object> επειδή οι δύο τύποι είναι στιγμιότυπα του ίδιου γενικού τύπου, αλλά για διαφορετικός πληκτρολογήστε τα ορίσματα και για αυτό το λόγο δεν είναι συμβατά μεταξύ τους.
Οι παραμετροποιημένοι τύπου σκυροδέματος είναι συγκεκριμένες περιπτώσεις της γενικής τυπού. Είναι σχεδόν σαν παιδιά; υπάρχουν μόνο κάποιοι περιορισμοί. ΔΕΝ ΜΠΟΡΟΥΝ να χρησιμοποιηθούν για τους ακόλουθους σκοπούς:
- για δημιουργία πινάκων
- σε χειρισμό εξαίρεσης
- σε μια τάξη κυριολεκτικά
- σε μια παράδειγμα του έκφραση
Τα συστήματα που περιέχουν στοιχεία των οποίων ο τύπος είναι συγκεκριμένος παραμετροποιημένος τύπος είναι παράνομα.
Παράδειγμα:
τεστ στατικού κενού() {
Pair< Ακέραιος, Ακέραιος>[] intPairArr = νέο Ζεύγος<Ακέραιος,Ακέραιος>[10] ; // σφάλμα
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1];
Ακέραιος i = pair.getFirst();
pair.setSecond(i);
}
static void addElements( Αντικείμενο[] objArr) {
objArr[0] = νέο Pair<Integer,Integer>(0,0);
objArr[1] = νέο Pair<String,String>("",""); // θα πρέπει να αποτύχει με το ArrayStoreException
}
Ο μεταγλωττιστής απαγορεύει τη δημιουργία πινάκων των οποίων ο τύπος συστατικού είναι συγκεκριμένος παραμετροποιημένος τύπος, όπως Pair< Ακέραιος, Ακέραιος> στο παράδειγμά μας. Συζητήσαμε στο προηγούμενο λήμμα γιατί είναι λογικό ο μεταγλωττιστής να πληροί τις προϋποθέσεις α Pair< Ακέραιος, Ακέραιος>[] ως παράνομο. Το βασικό πρόβλημα είναι ότι ο μεταγλωττιστής και το σύστημα χρόνου εκτέλεσης πρέπει να διασφαλίζουν ότι ένας πίνακας είναι α ομοιογενής ακολουθία στοιχείων του ίδιου τύπου. Ένας από τους ελέγχους τύπου, δηλαδή ο έλεγχος αποθήκευσης συστοιχιών που εκτελείται από την εικονική μηχανή κατά το χρόνο εκτέλεσης, αποτυγχάνει να εντοπίσει την προσβλητική εισαγωγή ενός εξωγήινου στοιχείου. Στο παράδειγμα η δεύτερη εισαγωγή στο addElements Η μέθοδος θα πρέπει να αποτύχει επειδή πρόσθεσε ένα ζεύγος συμβόλων σε έναν πίνακα τιμών, αλλά δεν απέτυχε όπως αναμενόταν. Οι λόγοι και και και στο περισσότερα λήμμα.
Εάν δεν μπορούμε να χρησιμοποιήσουμε πίνακες που περιέχουν στοιχεία των οποίων ο τύπος είναι συγκεκριμένος παραμετροποιημένος τύπος, τι χρησιμοποιούμε ως λύση;
Ας εξετάσουμε 3 πιθανούς τρόπους αντιμετώπισης:
- σειρά ακατέργαστων τύπων
- πίνακας απεριόριστου παραμετροποιημένου τύπου μπαλαντέρ
- συλλογή αντί για πίνακα
Ενώ οι συστοιχίες παραμετροποιημένων τυποσκυροδέματος είναι παρανομές, πιράτρεπτονται οι σιλμάτης παραμετροποιημένων τυποσκυροδέματος.
Ένας παραμετροποιημένος τύπος ψηφοδελτίου είναι μια παρουσίαση γενικού τύπου όπου τουλάχιστον ένα σφάλμα του τύπου είναι ψηφοδέλτιο. Παραδείγματα παραμετροποιημένων τύπος χαρακτοριομή παλαντέρ είναι Συλλογή<?> , List<; επεκτείνει τον Αριθμό> , Συγκριτής<; super String> και Pair<String,?> . Ένας παραμετροποιημένος τύπος χαρακτήρων μπαλαντέρ υποδηλώνει μια οικογένεια τύπων που περιλαμβάνει συγκεκριμένες περιπτώσεις ενός γενικού τύπου. Το είδος του μπαλαντέρ που χρησιμοποιείται καθορίζει ποιοι συγκεκριμένοι παραμετροποιημένοι τύποι ανήκουν στην οικογένεια. Για παράδειγμα, ο παραμετροποιημένος τύπος μπαλαντέρ Συλλογή<?> δηλώνει την οικογένεια όλων των στιγμιότυπων του Συλλογή διεπαφή ανεξάρτητα από το όρισμα τύπου. Ο παραμετροποιημένος τύπος μπαλαντέρ List<; επεκτείνει τον Αριθμό> υποδηλώνει την οικογένεια όλων των τύπων λίστας όπου ο τύπος στοιχείου είναι υποτύπος του Αριθμός . Ο παραμετροποιημένος τύπος μπαλαντέρ Συγκριτής<; super String> είναι η οικογένεια όλων των στιγμιότυπων του Συγκριτής διεπαφή για τύπους ορισμάτων που είναι υπερτύποι Σειρά.
Ένας παραμετροποιημένος τύπος μπαλαντέρ δεν είναι τύπος με την κανονική έννοια (διαφορετικός από μια μη παραμετροποιημένη κλάση/διεπαφή ή έναν ακατέργαστο τύπο).
Οι παραμετροποιημένοι τύποι μπαλαντέρ μπορούν να χρησιμοποιηθούν για πληκτρολόγηση (όπως μη παραμετροποιημένες κλάσεις και διεπαφές):
- ως όρισμα και τύποι μεθόδων επιστροφής
- ως τύπος πεδίου ή τοπικής μεταβλητής αναφοράς
- ως τύπος συστατικού ενός ορίσματος τύπου arrayas άλλων παραμετροποιημένων τύπων
- ως τύπος στόχου σε εκμαγεία
Όχι μόνο οι τύποι μπορούν να είναι γενικοί, αλλά και οι μέθοδοι μπορούν να είναι γενικές. Οι στατικές και μη στατικές μέθοδοι καθώς και οι κατασκευαστές μπορούν να έχουν παραμέτρους τύπου. Η σύνταξη για τη δήλωση τυπικών παραμέτρων είναι παρόμοια με τη σύνταξη για τους γενικούς τύπους. Το τμήμα της παραμέτρου του τύπου χωρίζεται με αγκύλες και εμφανίζεται πριν από τον τύπο επιστροφής της μεθόδου. Η σύνταξη και η σημασία του είναι πανομοιότυπα με τη λίστα των παραμέτρων ενός γενικού τύπου.
Εδώ είναι ένα γενικό παράδειγμα μέγ μέθοδος που υπολογίζει τη μεγαλύτερη τιμή σε μια συλλογή στοιχείων αγνώστου τύπου ΕΝΑ .
Παράδειγμα (γενική μέθοδος):
class συλλογές {
δημόσια στατική Το <A επεκτείνει Συγκρίσιμο<A>> A μέγ.(Συλλογή< ΕΝΑ > xs) {
Iterator< ΕΝΑ > xi = xs.iterator();
ΕΝΑ w = xi.next();
ενώ (xi.hasNext()) {
ΕΝΑ x = xi.next();
αν (w.compareTo(x) < 0) w = x;
}
επιστροφή w;
}
}
Ο μέγ Η μέθοδος έχει μία παράμετρο τύπου, με όνομα ΕΝΑ . Είναι μια θέση υποδοχής για τον τύπο στοιχείου της συλλογής στον οποίο λειτουργεί η μέθοδος. Η παράμετρος τύπου έχει ένα όριο. πρέπει να είναι ένας τύπος που είναι υποτύπος του Συγκρίσιμο<A>
Παράδειγμα (του ίδιου γενικού τύπου, αυτή τη φορά με όρια):
δημόσια διεπαφή Συγκρίσιμο<T> {
public int compareTo(T arg);
}
δημόσια κλάση TreeMap<Key επεκτείνεται Comparable<Key> ,Data>{
ιδιωτική στατική κλάση Entry<K,V> {
K κλειδί?
Τιμή V;
Entry<K,V> αριστερά = null;
Entry<K,V> δεξιά = null;
Entry<K,V> γονικό;
}
ιδιωτικό μεταβατικό Entry<Key,Data> root = null;
...
ιδιωτικό Entry<Key,Data> getEntry(κλειδί κλειδιού) {
Entry<Key,Data> p = root;
Κλειδί k = κλειδί;
ενώ (p != null) {
int cmp = k. compareTo(p.key) ;
εάν (cmp == 0)
επιστροφή p;
αλλιώς εάν (cmp < 0)
p = p.αριστερά;
αλλού
p = p.δεξιά;
}
επιστροφή null?
}
δημόσιο boolean containKey(Key key) {
επιστροφή getEntry(κλειδί) != null;
}
...
}
Στο παραπάνω παράδειγμα, η παράμετρος τύπου Κλειδί έχει το δεσμευμένο Συγκρίσιμο<Key> . Η προδιαγραφή ενός ορίου έχει δύο αποτελέσματα:
- Παρέχει πρόσβαση στις μεθόδους που καθορίζει το όριο . Στο παράδειγμα, το δεσμευμένο Συγκρίσιμο<Key> δίνει πρόσβαση στο σύγκρισηΜε μέθοδο που θέλουμε να επικαλεστούμε κατά την εφαρμογή μας TreeMap τάξη.
- Για την παρουσίαση του γενικού τύπου μπορούν να χρησιμοποιηθούν μόνο οι τύποι “εντός ορίων”. Στο παράδειγμα, ένας παραμετροποιημένος τύπος όπως TreeMap<Number,String> θα απορριφόταν, επειδή ο τύπος Αριθμός δεν είναι υποτύπος του Συγκρίσιμο<Αριθμός> . Ένας παραμετροποιημένος τύπος όπως TreeMap<String,String> θα γινόταν αποδεκτό, επειδή ο τύπος String είναι εντός ορίων, δηλ. είναι υποτύπος του Συγκρίσιμο<String> .
Όλες οι κλάσεις, οι διεπαφές και οι τύποι enum μπορούν να χρησιμοποιηθούν ως παράμετροι δεσμευμένου τύπου, συμπεριλαμβανομένων των ένθετων και εσωτερικών τύπων. Ούτε πρωτόγονοι τυπό ούτε οι τουπι τοιπό are used as bound parameters of a typu.
Παραδείγματα (τυπό ορίων παραμέτρων):
η κλάση X0 <T εκτείνεται ενθ > { ... } // σφάλμα
η κλάση X1 <T εκτείνεται Αντικείμενο[] > { ... } // σφάλμα
η κλάση X2 <T εκτείνεται Αριθμός > { ... }
η κλάση X3 <T εκτείνεται Σειρά > { ... }
η κλάση X4 <T εκτείνεται Δυνατότητα λειτουργίας > { ... }
η κλάση X5 <T εκτείνεται Νήμα.Κατάσταση > { ... }
η κλάση X6 <T εκτείνεται Λίστα > { ... }
η κλάση X7 <T εκτείνεται List<String> > { ... }
η κλάση X8 <T εκτείνεται List<; επεκτείνει τον Αριθμό> > { ... }
η κλάση X9 <T εκτείνεται Συγκρίσιμο <; super Number> > { ... }
η κλάση X10<T εκτείνεται Χάρτης.Είσοδος<?,?> > { ... }
Το δείγμα κώδικα δείχνει ότι οι πρωτόγονοι τύποι όπως π.χ ενθ και τύπους συστοιχιών όπως Αντικείμενο[] δεν επιτρέπεται ως δεσμευμένες παράμετροι του τύπου.
Τιποι ταξεόνες, όπως π.χ Αριθμός ή Σειρά και τύπους διεπαφής, όπως π.χ Δυνατότητα λειτουργίας .
Enum τύποι όπως π.χ Νήμα.Κατάσταση επιτρέπονται επίσης ως δεσμευμένες παραμέτρους τύπου. Νήμα.Κατάσταση Είναι ένα παράδειγμα ενός τύπου τύπου που χρησιμοποιείται ως όρος για έναν τύπο τύπου. Επιτρεπτονται της μη στατικης εσωτερικοι τυπος.
Οι πρωτόγονοι τύποι επιτρέπονται ως δεσμευμένες παράμετροι του τύπου. Λίστα είναι ένα παράδειγμα.
Οι παραμετροποιοι τυπος πραματροδεματος π.χ List<String> , περιορισμένους παραμετροποιημένους τύπους μπαλαντέρ όπως π.χ List<; επεκτείνει τον Αριθμό> και Συγκρίσιμο <; σούπερ Long> , και απεριόριστους παραμετροποιημένους τύπους μπαλαντέρ όπως π.χ Χάρτης.Είσοδος<?,?> . Ένα όριο που είναι τύπος παραμέτρου επιτρέπει ως παράμετρο όλους τους τύπους που ανήκουν στην οικογένεια των τύπων που υποδεικνύουν έναν χαρακτήρα. Το φράγμα του παραμετροποιημένου τύπου balanter παρέχει μόνο περιορισμένη πρόσβαση σε πεδία και μεθόδους. Οι περιορισμοί εξαρτώνται από τον τύπο του παίκτη.
Παράδειγμα (του τύπου του χαρακτήρα που βασίζεται σε παραμέτρους που επιλέγεται ως παράμετρος παραμέτρου του τύπου):
κλάση X< Το T επεκτείνει το List<; επεκτείνει τον Αριθμό> > {
δημόσιο κενό someMethod(T t) {
t.add(new Long(0L)); // σφάλμα
Αριθμός n = t.remove(0);
}
}
Δοκιμή τάξης {
δημόσιο στατικό κενό main(String[] args) {
X<ArrayList< Μακρύς >> x1 = νέο X<ArrayList<Long>>();
X<ArrayList< Σειρά >> x2 = νέο X<ArrayList<String>>(); // σφάλμα
}
}
Μεταβλητές αναφοράς τύπου Τ (η παράμετρος τίπου) are treated as variable reporting τίπου παλαντέρ (η παράμετρος τίπου δεσμευμένη). Στο παραδειγμά, η συνέπεια είναι οτι ο μεταγλωττιστής απόριπτει τις επικλήσεις πολέμων που περισσότερα με οριστικά τίπου “άγνωστου” που που παράμετρος τίπου, π.χ. List.add , γιατί το όριο είναι παραμετροποιημένος τύπος παλαντέρ με άνω φράγμα.
Ταυτόχρονα το δεσμευμένο List<; επεκτείνει τον Αριθμό> καθορίζει τους τύπους που μπορούν να χρησιμοποιηθούν ως ορίσματα τύπου. Ο μεταγλωττιστής δέχεται όλα τα ορίσματα τύπου που ανήκουν στην οικογένεια τύπων List<; επεκτείνει τον Αριθμό> , δηλαδή όλοι οι υποτύποι του Λίστα με όρισμα τύπου που είναι υποτύπος του Αριθμός .
Μια παράμετρος ενός τύπου μπορεί να χρησιμοποιηθεί ως όριο μιας άλλης παραμέτρου ενός τύπου.
Example (μιας παράμετρο τυπό που ως δεσμευμένη παράμετρο τυπό):
τάξη Τριπλή <T> {
private T fst, snd, trd;
δημόσιο < U επεκτείνει το Τ , V επεκτείνει το Τ , W επεκτείνει το Τ > Triple(U arg1, V arg2, W arg3) {
fst = arg1;
snd = arg2;
trd = arg3;
}
}
Σε αυτό το παράδειγμα η παράμετρος τύπου Τ της παραμετροποιημένης κλάσης χρησιμοποιείται ως δεσμευμένο των παραμέτρων τύπου U , V και W ένα παράδειγμα μιας μεθόδου αυτής της κλάσης.
Οι πρόσθετες ευκαιρίες για τη χρήση ενός τύπου παραμέτρου ως ορίου άλλου τύπου περιλαμβάνουν καταστάσεις όπου ένας τύπος ορίζεται σε έναν γενικό τύπο ή μια τοπική κλάση ορίζεται σε μια γενική μέθοδο. Επιτρέπεται ακόμη και η χρήση μιας παραμέτρου του ίδιου τύπου ως άλλης παραμέτρου τύπου στην ενότητα παραμέτρων του ίδιου τύπου.
Ένα όριο που είναι κλάση δίνει πρόσβαση σε όλα τα δημόσια μέλη της, δηλαδή δημόσια πεδία, μεθόδους και τύπους στοιχείων. Μόνο οι κατασκευαστές δεν είναι προσβάσιμοι επειδή δεν υπάρχει εγγύηση ότι μια υποκατηγορία του ορίου έχει τους ίδιους κατασκευαστές με τη δεσμευμένη.
Example (μιας κλάσης που ως δεσμός της παράμετρο τυπό):
δημόσια τάξη SuperClass { // στατικά μέλη
δημόσιος αριθμός EnumType {THIS, THAT}
δημόσιο στατικό Αντικείμενο staticField;
public static void staticMethod() { ... }// μη στατικά μέλη
δημόσια τάξη InnerClass { ... }
δημόσιο αντικείμενο nonStaticField.
public void nonStaticMethod() { ... }// κατασκευαστές
δημόσια SuperClass() { ... }// ιδιωτικά μέλη
ιδιωτικό αντικείμενο privateField;...
}η δημόσια τελική κλάση SomeClass<T επεκτείνεται SuperClass > {
ιδιωτικό αντικείμενο Τ.
public SomeClass(T t) { object = t; }δημόσια συμβολοσειρά toString() {
απόδοση
"στατικός ένθετος τύπος : "+T.EnumType.class+"\n"
+"στατικό πεδίο : "+T.staticField+"\n"
+"στατική μέθοδος : "+T.staticMethod()+"\n"
+"μη στατικός ένθετος τύπος: "+T.InnerClass.class+"\n"
+"μη στατικό πεδίο : "+object.nonStaticField+"\n"
+"μη στατική μέθοδος : "+object.nonStaticMethod()+"\n"
+"κατασκευαστής : "+(new T())+"\n" // σφάλμα
+"ιδιωτικό μέλος : "+object.privateField+"\n" // σφάλμα
;
}
}
Το δεσμευμένο SuperClass δίνει πρόσβαση στους ένθετους τύπους, στα στατικά πεδία και μεθόδους και στα μη στατικά πεδία και μεθόδους. Μόνο ο κατασκευαστής δεν είναι προσβάσιμος. Αυτό συμβαίνει επειδή οι κατασκευαστές δεν κληρονομούνται. Κάθε υποκλάση ορίζει τους δικούς της κατασκευαστές και δεν χρειάζεται να υποστηρίζει τους κατασκευαστές της υπερκλάσης της. Ως εκ τούτου, δεν υπάρχει καμία εγγύηση ότι μια υποκατηγορία του SuperClass Θα έχει τον ίδιο κατασκευαστή με την superclass του.
Το πλαίσιο στο οποίο Το Enum<E επεκτείνει το Enum<E>> Το ” φαίνεται να είναι η δήλωσή του Enum τάξη σε συσκευασία java.lang :
δημόσια αφηρημένη τάξη Το Enum<E επεκτείνει το Enum<E>> {
...
}
Ο τύπος Enum είναι η κοινή βασική κλάση όλων των τύπων απαρίθμησης. Στην Java ένας τύπος απαρίθμησης όπως Χρώμα μεταφράζεται σε τάξη Χρώμα που εκτείνεται Enum<Color> . Ο σκοπός της υπερτάξεως Enum Είναι ένα χαρακτηριστικό της λειτουργικότητας που είναι κοινό σε όλους τους τύπους απαρίθμησης.
Εδώ είναι ένα σκίτσο της τάξης Enum :
δημόσια αφηρημένη τάξη Enum< μι επεκτείνει το Enum<E>> υλοποιεί το Comparable< μι >, Serializable {
ιδιωτικό τελικό όνομα συμβολοσειράς?
δημόσιο τελικό όνομα συμβολοσειράς() { ... } ιδιωτική τελική int τακτική?
δημόσιο τελικό int ordinal() { ... }προστατευμένο Enum (Όνομα συμβολοσειράς, σε σειρά) { ... }
δημόσια συμβολοσειρά toString() { ... }
δημόσιο τελικό boolean ισούται με (Άλλο αντικείμενο) { ... }
δημόσιο τελικό int hashCode() { ... }
προστατευμένο τελικό Αντικείμενο clone() ρίχνει το CloneNotSupportedException { ... }
δημόσιο τελικό int compareTo( μι ο) {...}δημόσιο τελικό Class< μι > getDeclaringClass() { ... }
δημόσιο στατικό <T επεκτείνει Enum<T>> T valueOf(Class<T> enumType, όνομα συμβολοσειράς) { ... }
}
Το εκπληκτικό χαρακτηριστικό στη δήλωση “ Το Enum<E επεκτείνει το Enum<E>> Το ” είναι το γεγονός που ορίστηκε πρόσφατα η κλάση Enum και η παράμετρος τύπου που ορίστηκε πρόσφατα μι εμφανίζονται στο όριο της παραμέτρου του ίδιου τύπου. Σημαίνει ότι το Enum ο τύπος πρέπει να δημιουργηθεί για έναν από τους υποτύπους του. Για να καταλάβετε γιατί αυτό έχει νόημα, σκεφτείτε ότι κάθε τύπος enum μεταφράζεται σε έναν υποτύπο του Enum .
Εδώ είναι ένα enum Χρώμα :
αρίθμηση Χρώμα {ΚΟΚΚΙΝΟ, ΜΠΛΕ, ΠΡΑΣΙΝΟ}
Ο μεταγλωττιστής το μεταφράζει στην ακόλουθη κλάση:
δημόσια τελική τάξη Χρώμα εκτείνεται Enum<Color> {
public static final Color[] values() { return (Color[])$VALUES.clone(); }
δημόσιο στατικό Χρώμα valueOf(Όνομα συμβολοσειράς) { ... } private Color(String s, int i) { super(s, i); }δημόσιο στατικό τελικό Χρώμα ΚΟΚΚΙΝΟ;
δημόσιο στατικό τελικό Χρώμα ΜΠΛΕ;
δημόσιο στατικό τελικό Χρώμα ΠΡΑΣΙΝΟ.ιδιωτικό στατικό τελικό Χρώμα $VALUES[];
στατικό {
ΚΟΚΚΙΝΟ = νέο Χρώμα ("ΚΟΚΚΙΝΟ", 0);
ΜΠΛΕ = νέο χρώμα ("ΜΠΛΕ", 1);
ΠΡΑΣΙΝΟ = νέο χρώμα ("ΠΡΑΣΙΝΟ", 2);
$VALUES = (νέο χρώμα[] { ΚΟΚΚΙΝΟ, ΜΠΛΕ, ΠΡΑΣΙΝΟ });
}
}
Η κληρονομιά έχει ως αποτέλεσμα ότι η Χρώμα Ο τύπος κληρονομεί όλες τις μεθόδους που εφαρμόζονται σε Enum<Color> . Μεταξύ αυτών είναι και το σύγκρισηΜε μέθοδος. Ο Χρώμα. σύγκρισηΜε μέθοδος θα πρέπει πιθανώς να λάβει α Χρώμα ως επιχείρημα. Για να γίνει αυτό στην τάξη Enum είναι γενικό και το Enum.compareTo η μέθοδος παίρνει Enum παράμετρος τυπό ‘s μι ως επιχείρημα. Ως αποτέλεσμα, πληκτρολογήστε Χρώμα που προέρχονται από Enum<Color> κληρονομεί α σύγκρισηΜε μέθοδος που χρειάζεται α Χρώμα και επιχείρημα, ακριβώς όπως πρέπει.
Αν αναλύσουμε τη δήλωση “ Το Enum<E επεκτείνει το Enum<E>> «Μπορούμε να δούμε ότι αυτό το μοτίβο έχει πολλές πτυχές.
Πρώτον, υπάρχει το γεγονός ότι ο παράμετρος τυπος που παρατίθεται είναι ο τῆς ο τύπος: “. Enum Το <E επεκτείνεται Enum <E>> Φροντίζει να υπάρχουν μόνο υποτύποι τύπου Enum επιτρέπονται ως ορίσματα τύπου. (Θεωρητικά, πληκτρολογήστε Enum θα μπορούσε να δημιουργηθεί στον εαυτό του, όπως στο Enum<Enum> , αλλά σίγουρα δεν προορίζεται και είναι δύσκολο να φανταστεί κανείς μια κατάσταση στην οποία μια τέτοια παρουσίαση θα ήταν χρήσιμη.)
Δεύτερον, υπάρχει ο τύπος οτι ο τύπος δεσμενομένου παράμετρον είναι ο παραμετροποιντός τύπος. Enum <E> , το οποίο χρησιμοποιεί την παράμετρο τύπου μι ως όρισμα τύπου του δεσμευμένου. Αυτή η δήλωση διασφαλίζει ότι η σχέση κληρονομικότητας μεταξύ ενός υποτύπου και μιας παρουσίασης του Enum είναι της μορφής Το X επεκτείνει το Enum<X> “. Ένα άτομο σαν το “ Το X επεκτείνει το Enum<Y> Το ” δεν μπορεί να δηλωθεί λόγω του είδους του σφάλματος Υ δεν θα ήταν εντός των ορίων? μόνο υποτύποι του Enum<X> είναι εντός των ορίων.
Τρίτον, υπάρχει το γεγονός ότι Enum είναι γενική καταρχήν. Σημαίνει ότι ορισμένες από τις μεθόδους της κλάσης Enum πάρτε ένα όρισμα ή επιστρέψτε μια τιμή αγνώστου τύπου (ή με άλλο τρόπο εξαρτάται από έναν άγνωστο τύπο). Όπως ήδη γνωρίζουμε, αυτός ο άγνωστος τύπος αργότερα θα είναι υποτύπος Χ του Enum<X> . Ως εκ τούτου, στον παραμετροποιημένο τύπο Enum<X> , αυτές οι μέθοδοι περιλαμβάνουν τον υποτύπο Χ , και κληρονομούνται στον υποτύπο Χ . Ο σύγκρισηΜε η μέθοδος είναι ένα παράδειγμα παρόμοιας μεθόδου. κληρονομήματα από την υπόκλαση σε μια υπόκλιση και μια συγκεκριμένη υπόθεση σε κάθε περίπτωση.
Συνοψίσσαντος, η statement “ Το Enum<E επεκτείνει το Enum<E>> Το ” μπορεί να αποκρυπτογραφηθεί ως: Enum είναι ένας γενικός τύπος που μπορεί να δημιουργηθεί μόνο για τους υποτύπους του και αυτοί οι υποτύποι θα κληρονομήσουν ορισμένες χρήσιμες μεθόδους, ορισμένες από τις οποίες λαμβάνουν ορίσματα συγκεκριμένα για υποτύπους (ή εξαρτώνται διαφορετικά από τον υποτύπο).
Παράμετροι τύπου κατώτερου ορίου κλάσεων
Οι παράμετροι τύπου μπορεί να έχουν πολλά όρια, όπως in η κλάση Box<T επεκτείνει τον αριθμό> {...} . Αλλά μια παράμετρος τύπου δεν μπορεί να έχει κάτω όριο, δηλαδή μια κατασκευή όπως π.χ κατηγορίας Box<T μεγάλος Αριθμός> {...} δεν επιτρέπεται. Γιατί όχι; Η απάντηση είναι: είναι άσκοπο περισσότερα δεν θα σου αγόραζε, αν επιτρεπόταν. Ας δούμε γιατί οι παράμετροι του τύπου του κατώτερου ορίου των κλάσεων προκαλούν σύγχυση διερευνώντας τι σημαίνει ανώτερο όριο σε μια παράμετρο του τύπου.
Το ανώτερο όριο μιας παραμέτρου τύπου έχει τρία αποτελέσματα:
- Περιορισμένη εφαρμογή. Το άνω όριο περιορίζει το σύνολο των τύπων που μπορούν να χρησιμοποιηθούν για την εισαγωγή του γενικού τύπου. Αν δηλώσουμε α η κλάση Box<T επεκτείνει τον αριθμό> {...} τότε ο μεταγλωττιστής θα εξασφάλιζε ότι μόνο υποτύποι του Αριθμός μπορεί να χρησιμοποιηθεί ως όρισμα τύπου. Δηλαδή α Box<Number> ή α Box<Long> επιτρέπεται, αλλά α Box<Object> ή Box<String> θα απορριφθεί
Παράδειγμα (περιορισμένης παρουσίασης λόγω ενός άνω ορίου σε μια παράμετρο τύπου):
η κλάση Box<T επεκτείνει τον αριθμό> {
ιδιωτική τιμή Τ;
public Box(T t) { value = t; }
...
} Δοκιμή τάξης {
δημόσιο στατικό κενό main(String[] args) {
Box<Long> boxOfLong = νέο Box<Long>(0L); // μια χαρά
Box<String> boxOfString = νέο Box<String>(""); // σφάλμα: Η συμβολοσειρά δεν είναι εντός ορίων
}
}
- Πρόσβαση σε μη στατικά μέλη. Το άνω όριο δίνει πρόσβαση σε όλες τις δημόσιες μη στατικές μεθόδους και πεδία του άνω ορίου. Στην εφαρμογή μας η κλάση Box<T επεκτείνει τον αριθμό> {...} μπορούμε να καλέσουμε όλες τις δημόσιες μη στατικές μεθόδους που ορίζονται στην κλάση Αριθμός , όπως π.χ intValue() για παράδειγμα. Χωρίς το άνω όριο, ο μεταγλωττιστής θα απέρριπτε οποιαδήποτε τέτοια επίκληση. Παράδειγμα (πρόσβασης σε μη στατικά μέλη λόγω άνω ορίου σε μια παράμετρο τύπου):
η κλάση Box<T επεκτείνει τον αριθμό> {
ιδιωτική τιμή Τ;
public Box(T t) { value = t; }
public int increment() { return value.intValue()+1; } // <= θα ήταν σφάλμα χωρίς το δεσμευμένο αριθμό
...
}
- Τύπος Διαγραφή. Το αριστερό άνω όριο χρησιμοποιείται για τη διαγραφή τύπου και αντικαθιστά την παράμετρο τύπου στον κωδικό byte. Στο δικό μας η κλάση Box<T επεκτείνει τον αριθμό> {...} όλες οι εμφανίσεις του Τ θα αντικατασταθεί από το άνω όριο Αριθμός . Για παράδειγμα, εάν η τάξη Κουτί έχει ιδιωτικό πεδίο τύπου Τ και μια μέθοδος κενό σύνολο (περιεχόμενο Τ) για τον ορισμό αυτού του ιδιωτικού πεδίου, τότε το πεδίο θα είναι τύπου Αριθμός μετά τη διαγραφή τύπου και η μέθοδος θα μεταφραστεί σε μέθοδο κενό σύνολο (αριθμητικό περιεχόμενο) .
Παράδειγμα (χρησιμοποιώντας το ανώτερο όριο σε έναν τύπο παραμέτρου στο διάγραμμα τύπου – προτού διαγραφή τύπου):
κατηγορίας Box< Το T επεκτείνει τον Αριθμό > {
ιδιωτικός Τ αξία;
δημόσιο κουτί( Τ t) { value = t; }
...
}
- Παράδειγμα (χρησιμοποιώντας το ανώτερο όριο σε έναν τύπο παραμέτρου στο διάγραμμα τύπου –
μετά
- διαγραφή τύπου):
τάξη κουτί {
ιδιωτικός Αριθμός αξία;
δημόσιο κουτί( Αριθμός t) { value = t; }
...
}
- Επιπλέον, το αριστερό άνω όριο εμφανίζεται σε περαιτέρω θέσεις, όπως αυτόματα εισαγόμενα εκμαγεία και μέθοδοι γέφυρας.
Εάν επιτρέπονταν τα κατώτερα όρια στις παραμέτρους τύπου, ποιες παρενέργειες θα είχαν ή θα έπρεπε; Αν μια κατασκευή όπως κατηγορίας Box<T μεγάλος Αριθμός> {...} Επιτρέπεται Τι σημαίνουν οι 3 παρενέργειες μιας παραμέτρου ανώτερου ορίου του –, περιορισμένη παρουσίαση, πρόσβαση σε μη στατικό μέλος, διαγραφή του τύπου – για ένα κατώτερο όριο.
Περιορισμένες εφαρμογές. Ο μεταγλωττιστής θα μπορούσε να περιορίσει το σύνολο των τύπων που μπορούν να χρησιμοποιηθούν για την παρουσίαση του γενικού τύπου με μια παράμετρο τύπου χαμηλότερου ορίου. Για παράδειγμα, ο μεταγλωττιστής θα μπορούσε να επιτρέψει στιγμιότυπα όπως Box<Number> και Box<Object> από α Box<T μεγάλος Αριθμός> και να απορρίψετε στιγμιότυπα όπως Box<Long> ή Box<Short> . Αυτό θα ήταν ένα αποτέλεσμα σύμφωνο με την περιοριστική παρενέργεια που περιγράφεται για τα ανώτερα όρια παραμέτρων τύπου.
Πρόσβαση σε μη στατικά μέλη. Ένα όριο παραμέτρου κατώτερου τύπου δεν παρέχει πρόσβαση σε συγκεκριμένες μεθόδους πέρα από αυτές που έχουν κληρονομηθεί από την κλάση Αντικείμενο . Στο παράδειγμα του Box<T μεγάλος Αριθμός> οι υπερτύποι των Αριθμός δεν έχουν τίποτα κοινό, εκτός από το ότι είναι τύποι αναφοράς και επομένως υποτύποι του Αντικείμενο . Ο μεταγλωττιστής δεν μπορεί να υποθέσει ότι το πεδίο του τύπου Τ είναι τύπου Αριθμός ή υποτύπου αυτού. Αντίθετα, το πεδίο του τύπου Τ μπορεί να είναι οποιουδήποτε υπερτύπου Αριθμός , όπως π.χ Σειριοποιήσιμο ή Αντικείμενο . Η επίκληση μιας μεθόδου όπως π.χ intValue() δεν είναι πλέον ασφαλές για τον τύπο και ο μεταγλωττιστής θα πρέπει να το απορρίψει. Κατά συνέπεια, το όριο παραμέτρου κατώτερου τύπου δεν θα έδινε πρόσβαση σε μη στατικά μέλη πέρα από αυτά που ορίζονται στην κλάση Αντικείμενο Και έτσι έχει το ίδιο αποτέλεσμα με το όριο “ ”.
Τύπος Διαγραφή . Ακολουθώντας αυτή τη λογική, δεν έχει νόημα να αντικατασταθούν οι εμφανίσεις της παραμέτρου τύπου από το αριστερό κάτω όριο της. Δήλωση μιας μεθόδου όπως ο κατασκευαστής Κουτί( Τ t) ως κατασκευαστής Κουτί( Αριθμός t) δεν έχει νόημα, λαμβάνοντας υπόψη αυτό Τ αντικαθίσταται από έναν υπερτύπο του Αριθμός . Ενα Αντικείμενο θα μπορούσε δικαίως να μεταβιβαστεί στον κατασκευαστή σε μια στιγμή Box<Object> και ο κατασκευαστής θα το απέρριπτε. Αυτό θα ήταν λάθος. Έτσι, η διαγραφή τύπου θα αντικαταστήσει όλες τις εμφανίσεις της μεταβλητής τύπου Τ κατά τύπο Αντικείμενο , και όχι από το κατώτερο όριο του. Και πάλι, το κατώτερο όριο θα έχει το ίδιο αποτέλεσμα με το όριο “ ”.
Θέλετε να καταλάβετε τι σημαίνει αν χαμηλωθούν και τα δύο _και_ τα ανώτερα όρια επιτρέπονται. Προσωπικά, δεν θέλω καν να το σκέφτομαι και θα προτιμούσα να το βάλω στον διαχειριστή ” του “ αν το επιτρέψετε.
Η ουσία είναι: τολα τουσα είναι “ μεγάλος «Το δεσμευμένο θα σας αγόραζε είναι ο περιορισμός που μόνο οι υπερτύποι Αριθμός μπορούν να χρησιμοποιηθούν ως ορίσματα τύπου. Και ακόμη και αυτό συχνά παρεξηγείται. ΔΕΝ θα σήμαινε αυτό τάξη Box<T super Number> {...} περιέχει μόνο περιπτώσεις υπερτύπων του Αριθμός . Ακριβώς το αντίστροφο του – όπως φαίνεται στο παρακάτω παράδειγμα!
Παράδειγμα (χρησιμοποιώντας το ανώτερο όριο σε έναν τύπο παραμέτρου στο διάγραμμα τύπου – προτού διαγραφή τύπου):
κατηγορίας Box< Τ σούπερ Αριθμός > {
ιδιωτικός Τ αξία;
δημόσιο κουτί( Τ t) { value = t; }
...
}
Παράδειγμα (χρησιμοποιώντας το ανώτερο όριο σε έναν τύπο παραμέτρου στο διάγραμμα τύπου – μετά διαγραφή τύπου):
τάξη κουτί {
ιδιωτικός Αντικείμενο αξία;
δημόσιο κουτί( Αντικείμενο t) { value = t; }
...
}
ΕΝΑ κατηγορίας Box<T μεγάλος Αριθμός> {...} θα μεταφραζόταν με διαγραφή τύπου σε α Κουτί που περιέχει ένα Αντικείμενο πεδίο και ο κατασκευαστής του θα μεταφραστεί σε Πλαίσιο (Αντικείμενο t) . Αυτό είναι θεμελιωδώς διαφορετικό από το α κατηγορίας Box<T εκτείνεται Αριθμός> {...} , που θα μεταφραζόταν σε α Κουτί που περιέχει α Αριθμός πεδίο και ο κατασκευαστής του θα μεταφραστεί σε Πλαίσιο (Αριθμός t) . Κατά συνέπεια, α Box<Number> υποδεικνύεται από α η κλάση Box<T επεκτείνει τον αριθμό> {...} θα ήταν διαφορετικό από το α Box<Number> στιγμιότυπο από α τάξη Box<T super Number> {...} , που είναι πιθανό να προκαλέσει επιπλοκές. Για το λόγο αυτό, τα κάτω όρια δεν έχουν καμία σημασία στις παραμέτρους του τύπου κλάσης.
Παράμετροι τύπου κατώτερου ορίου των μεθόδων
Σε συνδυασμό με τις μεθόδους και τους τύπους των ορισμών τους, μπορεί περιστασιακά να είναι χρήσιμος ένας τύπος παραμέτρου με χαμηλότερο όριο.
Παράδειγμα (μια μέθοδος που θα επωφεληθεί από έναν τύπο παραμέτρου με χαμηλότερο όριο):
κλάση Pair<X,Y> {
ιδιωτικό Χ πρώτα?
ιδιωτικο Υ δευτερο?
...
δημόσιο < Ένα σούπερ Χ , Β σούπερ Υ > B addToMap(Map<A, B> map) { // σφάλμα: η παράμετρος τύπου δεν μπορεί να έχει κάτω όριο
επιστροφή map.put(first, second);
}
}
Δοκιμή τάξης {
δημόσιο στατικό κενό main(String[] args) {
Pair<String,Long> ζεύγος = νέο Pair<>("ABC",42L);
Map<CharSequence, Number> χάρτης = HashMap<CharSequence, Number>();
Αριθμός = pair.addToMap(map);
}
}
Ο addToMap() Η μέθοδος προσθέτει το περιεχόμενο του ζεύγους σε έναν χάρτη. Οποιοσδήποτε χάρτης που μπορεί να χωρέσει υπερτύπους Χ και Υ θα έκανε. Ο χάρτης βάζω() Η μέθοδος επιστρέφει την τιμή που βρέθηκε στον χάρτη για το δεδομένο κλειδί, εάν υπάρχει ήδη μια καταχώρηση κλειδιού-τιμής για το κλειδί στον χάρτη. Η επιστρεφόμενη τιμή του χάρτη βάζω() μέθοδος επιστρέφεται από το addToMap() μέθοδος. Κάτω από αυτές τις συνθήκες θα ήθελε κανείς να δηλώσει τη μέθοδο όπως φαίνεται παραπάνω: Ο χάρτης παραμετροποιείται με υπερτύπους των παραμέτρων τύπου του ζεύγους και addToMap() Ο τύπος επιστροφής της μεθόδου είναι ο τύπος τιμής του χάρτη.
Εφόσον ο μεταγλωττιστής δεν επιτρέπει χαμηλότερα όρια στις παραμέτρους του τύπου, χρειαζόμαστε λύση.
Μια λύση που έρχεται στο μυαλό είναι η χρήση χαρακτήρων μπαλαντέρ, επειδή οι χαρακτήρες μπαλαντέρ μπορεί να έχουν ένα χαμηλότερο όριο. Εδώ είναι μια λύση χρησιμοποιώντας ένα πρόχειρο.
Παράδειγμα (μία λύση για το προηγούμενο παράδειγμα χρησιμοποιώντας την προσωρινή μνήμη χαρακτήρων):
κλάση Pair<X,Y> {
ιδιωτικό Χ πρώτα?
ιδιωτικο Υ δευτερο?
...
κοινό Αντικείμενο addToMap(Map< ? Super X , ? σούπερ Υ χάρτης >) {
επιστροφή map.put(first, second);
}
}
Δοκιμή τάξης {
δημόσιο στατικό κενό main(String[] args) {
Pair<String,Long> ζεύγος = νέο Pair<>("ABC",42L);
Map<CharSequence, Number> χάρτης = HashMap<CharSequence, Number>();
Αριθμός αριθμός = (Αριθμός) pair.addToMap(map);
}
}
Λειτουργεί, εκτός από το ότι δεν υπάρχει τρόπος να δηλώσετε τον τύπο επιστροφής όπως επιθυμείτε. Θα ήταν ο υπερτύπος του Υ που ο μεταγλωττιστής καταγράφει από τον τυπο χρήτη, but there is no syntax for its definition. Δεν πρέπει να δηλώσουμε τον τύπο επιστροφής “ ? σούπερ Υ “γιατί” ? σούπερ Υ Το ” είναι παίκτης και όχι τύπος και επομένως δεν επιτρέπεται ως τύπος επιστροφής. Δεν έχουμε επιλογή και πρέπει να τη χρησιμοποιήσουμε Αντικείμενο αντί για τον τύπο επιστροφής της μεθόδου μας. Αυτός ο μάλλον αόριστος τύπος επιστροφής με τη σειρά του αναγκάζει τους καλούντες του addToMap() μέθοδο για τη μεταφορά της τιμής επιστροφής προς τα κάτω από Αντικείμενο στην αληθινή του μορφή. Δεν ήταν ακριβώς αυτό που είχαμε στο μυαλό μας.
Μια άλλη λύση είναι η χρήση στατικών μεθόδων. Εδώ είναι μια λύση με στατική αντί για μη στατική μέθοδο.
Παράδειγμα (μία λύση για το προηγούμενο παράδειγμα χρησιμοποιώντας στατική μέθοδο):
κλάση Pair<X,Y> {
ιδιωτικό Χ πρώτα?
ιδιωτικο Υ δευτερο?
...
κοινό στατικός < ΕΝΑ , σι ,Χ εκτείνεται η Α , Υ εκτείνεται Β > B addToMap(Pair<X,Y> pair, Map< ΕΝΑ , σι χάρτης >) {
επιστροφή map.put(pair.first,pair.second);
}
}
Δοκιμή τάξης {
δημόσιο στατικό κενό main(String[] args) {
Pair<String,Long> ζεύγος = νέο Pair<>("ABC",42L);
Map<CharSequence, Number> χάρτης = HashMap<CharSequence, Number>();
Αριθμός αριθμός = Ζεύγος .addToMap( ζεύγος ,χάρτης);
}
}
Το γενικό addToMap() Η μέθοδος έχει τέσσερις παραμέτρους τύπου: δύο σύμβολα κράτησης θέσης Χ και Υ για τον τύπο του ζεύγους και δύο σύμβολα κράτησης θέσης ΕΝΑ και σι για τον τύπο του χάρτη. ΕΝΑ και σι είναι υπερτύποι των Χ και Υ , γιατί Χ και Υ δηλώνονται με ΕΝΑ και σι ως άνω όριά τους. (Σημειώστε ότι οι παράμετροι τύπου της γενικής μεθόδου Χ και Υ δεν έχουν καμία σχέση με το Ζεύγος της τάξης Χ και Υ παραμέτρους. Τα ονόματα Χ και Υ χρησιμοποιούνται ξανά για τη γενική μέθοδο ώστε να αναγνωρίζονται εύκολα ως παράμετροι του τύπου του ζεύγους. Χρησιμοποιώντας τέσσερις παραμέτρους τύπου μπορούμε να δηλώσουμε τον ακριβή τύπο επιστροφής όπως επιθυμούμε: είναι ο ίδιος τύπος με την τιμή του χάρτη.
Παραδείγματα
τάξη SomeClass <T> {
// στατικός αρχικοποιητής, στατικό πεδίο, στατική μέθοδος
στατικός {
SomeClass< Τ Δοκιμή > = νέο SomeClass< Τ >(); // σφάλμα
}
ιδιωτικός στατικός Τ globalInfo; // σφάλμα
κοινό στατικός Τ getGlobalInfo() { // σφάλμα
επιστροφή globalInfo;
}
// μη στατικό αρχικοποιητή, μη στατικό πεδίο, μη στατική μέθοδος
{
SomeClass< Τ Δοκιμή > = νέο SomeClass< Τ >();
}
ιδιωτικός Τ localInfo;
κοινό Τ getLocalInfo() {
επιστροφή localInfo?
}
// στατικοί ένθετοι τύποι
κοινό στατικός τάξη Η αποτυχία επεκτείνει την εξαίρεση {
ιδιωτικός τελικός Τ πληροφορίες? // σφάλμα
δημόσια αποτυχία ( Τ t) { info = t; } // σφάλμα
κοινό Τ getInfo() {πληροφορίες επιστροφής; } // σφάλμα
}
ιδιωτικός διεπαφή Αντιγραφή {
Τ αντίγραφο(); // σφάλμα
}
ιδιωτικός αρίθμηση Κατάσταση {
ΕΓΚΥΡΟ, ΑΚΥΡΟ;
ιδιωτικός Τ πληροφορίες? // σφάλμα
public void setInfo( Τ t) { info = t; } // σφάλμα
κοινό Τ getInfo() {πληροφορίες επιστροφής; } // σφάλμα
}
// μη στατικοί ένθετοι τύποι
κοινό τάξη Accessor {
κοινό Τ getInfo() { return localInfo; }
}
}
Το παράδειγμα δείχνει ότι η παράμετρος τύπου δεν μπορεί να χρησιμοποιηθεί στο στατικό πλαίσιο μιας γενικής κλάσης. Δείχνει επίσης ότι οι τύποι enum και οι τύποι enum θεωρούνται μέλη του στατικού τύπου της κλάσης. Μόνο οι εσωτερικές κλάσεις, δηλαδή οι μη στατικές ένθετες κλάσεις, μπορούν να χρησιμοποιήσουν την παράμετρο τύπου της γενικής κλάσης που περιέχει.
Το εύρος του τύπου παραμέτρου μιας διεπαφής είναι ολόκληρος ο ορισμός μιας διεπαφής, εκτός από τυχόν πεδία ή τύπους. Αυτό συμβαίνει επειδή τα πεδία και οι τύποι που ορίζονται στη διεπαφή είναι έμμεσα στατικά.
Παράδειγμα:
διεπαφή SomeInterface <T> {
// πεδίο
SomeClass< Τ Τιμή > = νέο SomeClass< Τ >(); // σφάλμα
// ένθετος τύπος
τάξη Accessor {
κοινό Τ getInfo() { // σφάλμα
return value.getGlobalInfo();
}
}
// μεθόδους
Τ getValue();
}
Το παράδειγμα δείχνει ότι τα πεδία μιας διεπαφής είναι έμμεσα στατικά, έτσι ώστε η παράμετρος τύπου δεν μπορεί να χρησιμοποιηθεί οπουδήποτε στη δήλωση ενός πεδίου μιας γενικής διεπαφής. Ομοίως, η ένθετη κλάση θεωρείται μια στατική ένθετη κλάση, όχι μια εσωτερική κλάση, και για αυτόν τον λόγο η χρήση της παραμέτρου τύπου οπουδήποτε στην ένθετη κλάση είναι παράνομη.
Λύση για ένθετες στατικές κλάσεις και διεπαφές:
Στην περίπτωση κλάσεων και διεπαφών στατικού περιεχομένου, αυτό δεν είναι μεγάλη καταστροφή. Ως λύση, μπορούμε να δημιουργήσουμε την ίδια στατική κλάση ή διεπαφή.
Παράδειγμα (λύση – create τον ένθετο static type):
περιτύλιγμα τάξης <T> {
ιδιωτικός τελικός Τ το Αντικείμενο;
δημόσιο περιτύλιγμα( Τ t) { theObject = t; }
κοινό Τ getWrapper() { return theObject; } δημόσιος Αμετάβλητος <T> makeImmutable() {
επιστροφή νέου Αμετάβλητο <T> (το Αντικείμενο);
}
δημόσιο Μεταβλητό <T> makeMutable() {
επιστροφή νέου Μεταβλητού <T> (το Αντικείμενο);
}
ιδιωτικό στατικό <A> A makeClone(A theObject) { ... }δημόσια στατική τελική κλάση Immutable <A> { // είναι μια γενική τάξη τώρα
ιδιωτικός τελικός ΕΝΑ το Αντικείμενο;δημόσιο αμετάβλητο( ΕΝΑ arg) {
theObject = makeClone(arg);
}
κοινό ΕΝΑ getWrappedItem() {
επιστροφή makeClone(theObject);
}
}
δημόσια στατική κλάση Μεταβλητό <A> {
... παρόμοια ...
}
}
Δεν υπάρχει τέτοιος τρόπος αντιμετώπισης για ένθετους τύπους enum επειδή δεν μπορούν να είναι γενικοί.
Λύση για ένθετες στατικές κλάσεις:
Εάν ο στατικός τύπος είναι κλάση, μια εναλλακτική λύση θα ήταν η μετατροπή της στατικής κλάσης σε μια μη στατική εσωτερική κλάση.
Παράδειγμα (λύση – use an interior class):
περιτύλιγμα τάξης <T> {
ιδιωτικός τελικός Τ το Αντικείμενο;
δημόσιο περιτύλιγμα( Τ t) { theObject = t; }
κοινό Τ getWrapper() { return theObject; } δημόσιο Μεταβλητό makeMutable() {
απόδοση αυτό. new Mutable(theObject);
}
public Immutable makeImmutable() {
απόδοση αυτό. new Immutable(theObject);
}
ιδιωτικό στατικό <A> A makeClone(A theObject) { ... }δημόσια τελική κλάση Immutable { Η // δεν είναι πλέον στατική κλάση
ιδιωτικός τελικός Τ το Αντικείμενο;δημόσιο αμετάβλητο( Τ arg) {
theObject = makeClone(arg);
}
κοινό Τ getWrappedItem() {
επιστροφή makeClone(theObject);
}
}
δημόσια τάξη Μεταβλητό
... παρόμοια ...
}
}
Αυτή η λύση συνοδεύεται από ένα ορισμένο ποσό γενικών εξόδων επειδή όλες οι εσωτερικές κλάσεις έχουν μια κρυφή αναφορά σε μια εμφάνιση του εξωτερικού τύπου που ούτε χρειάζεται ούτε χρησιμοποιείται σε αυτό το παράδειγμα. η κυπλή περισσότερα ελληνικά απόσκεις. Συχνά, οι εσωτερικές κλάσεις συνδυάζονται με διεπαφές προκειμένου να διατηρηθεί η εσωτερική κλάση μια ιδιωτική λεπτομέρεια υλοποίησης της τάξης που περιβάλλει. Μπορούμε να κάνουμε το ίδιο εδώ.
Παράδειγμα (η προηγούμενη λύση διορθώθηκε):
περιτύλιγμα τάξης <T> {
ιδιωτικός τελικός Τ το Αντικείμενο;
δημόσιο περιτύλιγμα( Τ t) { theObject = t; }
κοινό Τ getWrapper() { return theObject; } δημόσιο Μεταβλητό <T> makeMutable() {
Επιστροφή this.new Mutable(theObject);
}
δημόσιος Αμετάβλητος <T> makeImmutable() {
επιστροφή this.new Immutable(theObject);
}
ιδιωτικό στατικό <A> A makeClone(A theObject) { ... }δημόσια διεπαφή Immutable<S> {
S getWrappedItem();
}
δημόσια διεπαφή Μεταβλητό<S> {
S getWrappedItem();
void setWrappedItem(S arg);
}
ιδιωτικός τελική κλάση ImmutableImplementation υλοποιεί το Immutable<T> {ιδιωτικό τελικό T theObject;
δημόσια ImmutableImplementation(T arg) {
theObject = makeClone(arg);
}
δημόσιο T getWrappedItem() {
επιστροφή makeClone(theObject);
}
}
ιδιωτικός class MutableImplementation υλοποιεί το Mutable<T> {
... παρόμοια ...
}
}
Όπως μπορείτε να δείτε, οι ένθετες δημόσιες διεπαφές πρέπει να είναι γενικές, ενώ οι εσωτερικές ιδιωτικές κλάσεις μπορούν να χρησιμοποιούν την παράμετρο τύπου της κλάσης που περικλείουν.

Αφήστε μια απάντηση