Ένας γενικός τύπος ορίζεται χρησιμοποιώντας μία ή περισσότερες μεταβλητές τύπων και έχει μία ή περισσότερες ρ ςμεθόδους που χρησιμοποιούν μεταβλητές τύπων ως σύμβολο που αντικαθιστάται από όνομα τύπου, είτε πρόκειται για παράμετρο είτε για τύπο επιστρεφόμενης τιμής. Για παράδειγμα ο τύπος java.util.list<e> είναι ένας γενικός τύπος: μία λίστα με στοιχεία ΚΑΠΟΙΟΥ τύπου που εκφράζεται από το σύμβολο E. Αυτός ο τύπος έχει μία μέθοδο add(), που δηλώνεται έτσι ώστε να παίρνει μία παράμετρο τύπου E, καθώς και μία μέθοδο get() που δηλώνεται με επιστρεφόμενη ρφμ τιμή τύπου E. Όταν χρησιμοποιούμε γενικούς τύπους όπως αυτός, καθορίζουμε το όνομα τύπου που αντικαθιστά τη μεταβλητή ή της μεταβλητές τύπων και έτσι παράγουμε έναν παραμετροποιημένο τύπο List<String>. Αυτό πρέπει να γίνει τη στιγμή που χρησιμοποιείται ο γενικός τύπος έτσι ώστε ο compiler να μπορεί να χρησιμοποιήσει αυτή την επιπλέον πληροφορία για να κάνει τους απαραίτητους ελέγχους που θα εξασφαλίσουν την επιτρεπτή ήχρήση της μνήμης. μήμηςένας τέτοιος έλεγχος αποτρέπει π.χ. να βάλουμε ένα String[], σε λίστα που προορίζεται για να περιέχει αντικείμενα String. 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 32
Οι συλλογές του πακέτου java.util package στην έκδοση 5.0 της Java και τις μεταγενέστερες έχουν υλοποιηθεί ως γενικοί τύποι και είναι ίσως οι πιο συχνά χρησιμοποιούμενοι γενικοί τύποι σε προγράμματα Java. Το πακέτο java.util περιλαμβάνει το Java Collections Framework που μας δίνει τη δυνατότητα να χρησιμοποιούμε προκατασκευασμένα sets, lists αντικειμένων και mappings από key objects σε value objects. Στη Java 5.0, όταν δηλώνουμε μία μεταβλητή List ως στιγμιότυπο της ArrayList, καθορίζουμε τον τύπο E των στοιχείων που θα περιέχει η λίστα βάζοντας το όνομά του σε < και > όπως και στη δήλωση του γενικού τύπου. Μία λίστα για συμβολοσειρές θα τη γράφαμε π.χ. List<String>. Τα στοιχεία των συλλογών java.util πρέπει να είναι αντικείμενα και δεν επιτρέπεται να χρησιμοποιούνται πρωτόγονοι τύποι. Έτσι, δεν επιτρέπεται για παράδειγμα να δηλωθεί ένα Set<char>, ή μία List<int>. 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 33
Παράδειγμα με την ArrayList() χωρίς γενικό τύπο (παλιά έκδοση Java) public static void main(string[] args) { // This list is intended to hold only strings. List wordlist = new ArrayList(); Παράδειγμα με την ArrayList() με γενικό τύπο (Java 5.0 και μετά) public static void main(string[] args) { // This list can only hold String objects List<String> wordlist = new ArrayList<String>(); // We added a String[] instead of a String. // The compiler doesn't know that this is an error. wordlist.add(args); // args is a String[], not String, so the compiler won't //let us do this wordlist.add(args); // Compilation error! // Since the list is intended to hold strings, we cast // We can do this, though. // the return value to String but get a // Notice the use of the new for/in looping statement //ClassCastException because of the error above. for(string arg : args) wordlist.add(arg); String word = (String)wordlist.get(0); Γίνονται ενδελεχείς // No cast is required. List<String>.get() returns a έλεγχοι από τον compiler // String. Runtime error String word = wordlist.get(0); 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 34
Γενικοί τύποι με πολλές μεταβλητές τύπων (διαβάστε για τα Maps: είναι πολλοί χρήσιμες συλλογές γςπου καταχωρούν ζεύγη κλειδί τιμή και έχουν δυνατότητα άμεσης προσπέλασης) public static void main(string[] args) { // A map from strings to their position in the args[] array Map<String,Integer> map = new HashMap<String,Integer>(); // Note that we use autoboxing to wrap i in an Integer object. for(int i=0; i < args.length; i++) map.put(args[i], i); // Find the array index of a word. Note no cast is required! Integer position = map.get("hello"); // We can also rely on autounboxing to convert directly to an int, // but this throws a NullPointerException if the key does not exist // in the map int pos = map.get("world"); 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 35
Γενικοί τύποι ως παράμετροι τύπων σε άλλους γενικούς τύπου: επιτρέπεται γιατί είναι και αυτοί τύποι // Look at all those nested angle brackets! Map<String, List<List<int[]>>> t<i t[]>>> map = getweirdmap(); // The compiler knows all the types and we can write expressions // like this without casting. We might still get NullPointerException // or ArrayIndexOutOfBounds at runtime, of course. int value = map.get(key).get(0).get(0)[0]; // Here's how we break that expression down step by step. List<List<int[]>> listoflists = map.get(key); List<int[]> listofintarrays = listoflists.get(0); int[] array = listofintarrays.get(0); int element = array[0]; 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 36
Ο τρόπος με τον οποίο δηλώνονται οι μέθοδοι στους γενικούς τύπους είναι γενικός, δηλ. μπορούν να εφαρμοστούν οποιαδήποτε και αν είναι η παράμετρος τύπου. Θα μπορούσαμε για παράδειγμα αντί να χρησιμοποιήσουμε την get να χρησιμοποιήσουμε τη μέθοδο iterator( ), που μπορεί να μας μεταφέρει μέσα στη λίστα από ένα στοιχείο στο επόμενο. Ο τύπος που επιστρέφει η μέθοδος καθορίζεται πλήρως στο σημείο που χρησιμοποιείται ο γενικός τύπος List<String> words = //...initialized elsewhere... Iterator<String> iterator = words.iterator(); String firstword = iterator.next(); 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 37
Γενικοί τύποι και wildcards Θέλουμε μία μέθοδο που θα εμφανίζει τα στοιχεία μιας λίστας (αλλά τι είδους λίστας αφού αυτή θα είναι ένας γενικός τύπος;) Θέλουμε να γράψουμε τη μέθοδο για όλες τις πιθανές λίστες!!!!!! Μπορούμε: public static void printlist(printwriter out, List<?> list) { for(int i=0, n=list.size(); i < n; i++) { if (i > 0) out.print( print(", "); Object o = list.get(i); List of unknown out.print(o.tostring()); Δεν μπορούμε όμως να χρησιμοποιούμε wildcards με constructors List<?> l = new ArrayList<?>(); // Error!! 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 38
Αν θέλουμε να δημιουργήσουμε ένα List που να παίρνει μέσα οποιοδήποτε τύπο αντικειμένου, τότε List<Object> l = new ArrayList<Object>(); Έστω ότι θέλουμε να γράψουμε μία μέθοδο sumlist() που να υπολογίζει το άθροισμα μιας λίστας από αντικείμενα Number. Πρέπει να λειτουργεί και με List<Integer> ή List<Double>. public static double sumlist(list<? extends Number> list) { double total = 0.0; for(number n : list) total += n.doublevalue(); return total; Number ή υποκλάση του Number 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 39
Πως ορίζουμε γενικούς τύπους Παράδειγμα import java.util.*; /* A tree is a data structure that holds values of type V. Each tree has a single value of type V and can have any number of branches, each of which is itself a Tree. */ public class Tree<V> { V value; List<Tree<V>> branches = new ArrayList<Tree<V>>(); public Tree(V value) { this.value = value; V getvalue() { return value; void setvalue(v value) { this.value = value; int getnumbranches() { return branches.size(); Tree<V> getbranch(int n) { return branches.get(n); void addbranch(tree<v> branch) { branches.add(branch); 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 40
Μπορούμε να χρησιμοποιήσουμε και wildcards σε γενικούς τύπους Παράδειγμα public class Tree<V> { V value; List<Tree<? extends V>> branches = new ArrayList<Tree<? extends V>>(); public Tree(V value) { this.value = value; V getvalue() { return value; void setvalue(v value) { this.value = value; int getnumbranches() { return branches.size(); Tree<? extends V> getbranch(int n) { return branches.get(n); void addbranch(tree<? extends V> branch) ){ branches.add(branch); ); Τώρα μπορούμε να προσθέσουμε ένα Τree<Integer>, για παράδειγμα ως κλαδί ενός Tree<Number> Tree<Number> t = new Tree<Number>(0); // Note autoboxing t.addbranch(new Tree<Integer>(1)); // int 1 autoboxed to Integer 13 Ιανουαρίου 2011 Αντικειμενοστρεφής Προγραμματισμός στη Java 41