java:generics

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
java:generics [2015/03/15 20:35]
gthanos [Παραμετρικοί τύποι δεδομένων με πολλές παραμέτρους]
java:generics [2017/02/17 15:01] (current)
gthanos
Line 1: Line 1:
 ====== Παραμετρικοί τύποι δεδομένων (Generics) ====== ====== Παραμετρικοί τύποι δεδομένων (Generics) ======
  
-Ένας ​//generic// (γενικός) τύπος δεδομένων είναι ένας τύπος δεδομένων ο οποίος μπορεί να λαμβάνει ως παραμέτρους άλλους τύπους δεδομένων. Δείτε το παρακάτω παράδειγμα της κλάσης Box, η οποία θέλουμε να μπορεί να αποθηκεύσει οποιονδήποτε τύπο δεδομένων.+Ένας ​παραμετρικός ​(//​generic//​) τύπος δεδομένων είναι ένας τύπος δεδομένων ο οποίος μπορεί να λαμβάνει ως παραμέτρους άλλους τύπους δεδομένων. ​Όταν θέλουμε να δημιουργήσουμε κλάσεις που μπορούν να αποθηκεύσουν αντικείμενα οποιασδήποτε κλάσης (π.χ. εάν θέλουμε να δημιουργήσουμε μία διασυνδεδεμένη λίστα ή μία στοίβα) τότε θα πρέπει τις κλάσεις αυτές να τις κάνουμε τόσο γενικές ώστε να μπορούν να αποθηκεύουν οποιονδήποτε τύπο αντικειμένων.  
 + 
 +Η υιοθέτηση γενικών κλάσεων έχει το πλεονέκτημα ότι μπορεί να αποθηκεύσει αντικείμενα οποιασδήποτε κλάσης,​ όμως απαιτεί αρκετές μετατροπές τύπων (typecasts). Οι μετατροπές τύπων όταν γίνονται από τον προγραμματιστή και όχι από τον compiler ενέχουν κινδύνους ως προς την ορθή μετατροπή. 
 + 
 +===== Παράδειγμα γενικής κλάσης για την αποθήκευση δεδομένων ===== 
 + 
 +Δείτε το παρακάτω παράδειγμα της κλάσης ​**Box**, η οποία θέλουμε να μπορεί να αποθηκεύσει οποιονδήποτε τύπο δεδομένων.
  
 <code java Box.java>​ <code java Box.java>​
Line 12: Line 18:
 </​code>​ </​code>​
  
-Στο παραπάνω παράδειγμα ​επειδή όλες οι κλάσεις κληρονομούν ​την κλάση Object, μπορείτε να περάσετε ως όρισμα στις μεθόδους ​της συγκεκριμένης κλάσης οποιοδήποτε τύπο δεδομένων. Δείτε το παρακάτω παράδειγμα κώδικα που χρησιμοποιεί την κλάση Box.+Ας υποθέσουμε ότι έχετε ​το παρακάτω παράδειγμα ​κώδικα που ​λειτουργεί σωστά αποθηκεύοντας στην κλάση ​Box και επιστρέφοντας την ​τιμή ενός ​αντικείμενου τύπου [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​Integer.html|Integer]].
  
 <code java BoxUsage.java>​ <code java BoxUsage.java>​
Line 20: Line 26:
      ​Integer n = new Integer(5);      ​Integer n = new Integer(5);
      ​b.set(n);​      ​b.set(n);​
-     String ​s = (String)b.get();+     Integer ​s = (Integer)b.get();
    }    }
 } }
 </​code>​ </​code>​
  
-Ο παραπάνω ​κώδικας αφού μεταγλωττιστεί ​και επιχειρήσουμε να το τρέξουμε παράγει το παρακάτω ​exception +Στο ​παραπάνω ​παράδειγμα επειδή όλες οι κλάσεις κληρονομούν την κλάση [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​Object.html|Object]],​ μπορείτε  
-<code+  * να περάσετε ως όρισμα στη μέθοδο //set// οποιοδήποτε τύπο δεδομένων ή  
-Exception in thread "​main" ​java.lang.ClassCastException: ​java.lang.Integer cannot be cast to java.lang.String +  * να αναθέσετε την επιστρεφόμενη τιμή της μεθόδου //get// σε οποιοδήποτε τύπο δεδομένων.  
-        ​at ​BoxUsage.main(BoxUsage.java:7)+ 
 +Παρατηρήστε ότι στην ​τελευταία γραμμή ​είμαστε υποχρεωμένοι να κάνουμε typecast την επιστρεφόμενη τιμή της μεθόδου get() από [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​Object.html|Object]] ​στον τύπο δεδομένων που τελικά έχει αποθηκεύσει η κλάση Box. Εάν δεν κάνουμε ​typecast ο μεταγλωττιστής διαμαρτύρεται. Εάν το typecast είναι σωστό ο κώδικας λειτουργεί σωστά, ενώ εάν το typecast είναι λάθος δημιουργείται ​ένα //​Exception//​ που περιγράφεται ​παρακάτω.  
 + 
 +Δείτε τώρα ​το παρακάτω ​παράδειγμα κώδικα που χρησιμοποιεί την κλάση Box για να αποθηκεύσει ένα Integer και στη συνέχεια λαμβάνει το περιεχόμενο του ακεραίου και επιχειρεί να το αποθηκεύει σε μία μεταβλητή τύπου String. 
 + 
 +<code java BoxUsage.java> 
 +public class BoxUsage ​
 +   ​public static void main(String args[]) { 
 +     Box b = new Box(); 
 +     ​Integer n = new Integer(5);​ 
 +     b.set(n)
 +     ​String s = (String)b.get();​ 
 +   } 
 +}
 </​code>​ </​code>​
  
-Παρατηρήστε επίσης, ότι ​στην τελευταία γραμμή είμαστε υποχρεωμένοι να κάνουμε cast την επιστρεφόμενη τιμή της μεθόδου get() από Object σε String. ​Προκειμένου να αποφύγουμε τα παραπάνω προβλήματα και να είμαστε σίγουροι ότι ο κώδικας που γράφουμε δεν περιέχει λάθος χρήση τύπων δεδομένων η Java είσαγει τους ​γενικούς τύπους,​ γνωστούς ως generics. Η κλάση Box με χρήση ​Generics ​θα μπορούσε να γραφεί ως εξής:+Η μετατροπή τύπου στην τελευταία γραμμή είναι προφανώς λάθος. Ο παραπάνω κώδικας αφού μεταγλωττιστεί και ​επιχειρήσουμε να το τρέξουμε παράγει ένα [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​ClassCastException.html|java.lang.ClassCastException]],​ διότι επιχειρούμε να αναθέσουμε μία μεταβλητή τύπου [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​String.html|String]] ένα αντικείμενο τύπου [[https://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​Integer.html|Integer]]. 
 + 
 +===== Παράδειγμα παραμετρικής κλάσης ===== 
 + 
 +Προκειμένου να αποφύγουμε τα παραπάνω προβλήματα και να είμαστε σίγουροι ότι ο κώδικας που γράφουμε δεν περιέχει λάθη στη ​χρήση τύπων δεδομένων η Java εισάγει τους ​παραμετρικούς τύπους ​δεδομένων, γνωστούς ​και ​ως Generics. Η κλάση Box με χρήση ​//​generics// ​θα μπορούσε να γραφεί ως εξής:
  
 <code java Box.java>​ <code java Box.java>​
Line 47: Line 70:
 </​code>​ </​code>​
  
-===== Ονοματολογία παραμέτρων =====+Η παραπάνω δήλωση της κλάσης //Box// σημαίνει ότι κατά τον ορισμό αντικειμένων της κλάσης αυτά θα πρέπει να προσδιορίζεται ανάμεσα στους χαρακτήρες '<',​ ΄>'​ ένας επιπλέον reference τύπος δεδομένων,​ δηλ μπορούμε να γράψουμε 
 + 
 +<code java> 
 +Box<​String>​ b1 new Box<​String>​();​ 
 +Box<​Integer>​ b2  ​new Box<​Integer>​();​ 
 +Box<​Student>​ b3 new Box<​Student>​();​ // όπου Student μία κλάση που έχουμε κατασκευάσει.  
 +</​code>​ 
 + 
 +Ο τύπος δεδομένων που χρησιμοποιούμε ανάμεσα στους χαρακτήρες '<',​ ΄>'​ είναι ο τύπος δεδομένων που επιτρέπεται να αποθηκευθεί στην κλάση //Box// κάθε φορά.  
 + 
 +===== Συνήθης ονοματολογία παραμέτρων =====
  
   * E - Element (χρησιμοποείται στο Java Collections Framework)   * E - Element (χρησιμοποείται στο Java Collections Framework)
Line 56: Line 89:
   * S,U,V etc. - 2nd, 3rd, 4th types   * S,U,V etc. - 2nd, 3rd, 4th types
  
-===== Δημιουργία και χρήση αντικειμένων ενός ​γενικού τύπου δεδομένων =====+===== Δημιουργία και χρήση αντικειμένων ενός ​παραμετρικού τύπου δεδομένων =====
  
 <code java> <code java>
Line 80: Line 113:
 </​code>​ </​code>​
  
-===== Παραμετρικοί τύποι δεδομένων με πολλές παραμέτρους ===== +Προηγούμενο: ​[[:java:class_file | Αρχεία ​]] | [[:toc | Περιεχόμενα ​]] | Επόμενο: [[:​java:​generic_interfaces | Interfaces ​ως παραμετρικοί τύποι δεδομένων ]] |
- +
-<code java Pair.java>​ +
-public interface Pair<K, V> { +
-    public K getKey(); +
-    public V getValue();​ +
-+
-</​code>​ +
- +
-<code java OrderedPair.java>​ +
-public class Pair<K, V> { +
- +
-    private K key; +
-    private V value; +
- +
-    public Pair(K key, V value) { +
-        this.key = key; +
-        this.value = value; +
-    } +
- +
-    public void setKey(K key) { this.key = key; } +
-    public void setValue(V value) { this.value = value; } +
-    public K getKey() ​  { return key; } +
-    public V getValue() { return value; } +
-+
- +
-</​code>​ +
- +
-Με βάση τον παραπάνω κώδικα ​μπορείτε να δημιουργήσετε αντικείμενα επιμέρους τύπων ως εξής: +
-<​code ​java OrderedPairUsage.java>​ +
-public class OrderedPairUsage { +
-  public static void main(String args[]) { +
-    Pair<​String,​ Integer> p1 = new OrderedPair<​String,​ Integer>​("​Even",​ 8); +
-    Pair<​String,​ String> ​ p2 = new OrderedPair<​String,​ String>​("​hello",​ "​world"​);​ +
-    OrderedPair<​String,​ Box<​Integer>>​ p = new OrderedPair<>​("​primes",​ new Box<​Integer>​());​ +
-    // the following is not allowed +
-    Pair<​String,​ Integer> p1 = new OrderedPair<>​("​hello",​ "​world"​);​ +
-  } +
-+
-</​code>​ +
- +
-===== Απλοί παραμετρικοί τύποι δεδομένων (Raw Generic Types) ===== +
- +
-Εάν δεν ​χρησιμοποιήσουμε καμία παράμετρο τότε καταλήγουμε να έχουμε έναν "απλό" παραμετρικό τύπο δεδομένων γνωστό ​ως **Raw types**. Η χρήση των τύπων αυτών δυσκολεύει τον compiler να αποφασίσει για την ασφαλή ή μη χρήση δεδομένων αυτού του τύπου, ειδικά όταν τα δεδομένα αυτά χρησιμοποιούνται μαζί με παραμετρικούς τύπους δεδομένων. Δείτε τα παρακάτω παραδείγματα κώδικα +
- +
-<code java> +
-Box<​String>​ stringBox = new Box<>​();​ +
-Box rawBox = stringBox; ​              // Safe - OK +
-</​code>​ +
- +
-<code java> +
-Box rawBox = new Box(); ​          // rawBox is a raw type of Box<​T>​ +
-Box<​Integer>​ intBox = rawBox; ​    // warning: unchecked conversion +
-</​code>​ +
- +
-<code java> +
-Box<​String>​ stringBox = new Box<>​();​ +
-Box rawBox = stringBox;​ +
-rawBox.set(8); ​             // warning: unchecked invocation to set(T) +
-</​code>​ +
- +
-==== Unchecked Error Messages ==== +
- +
-Όπως είπαμε προηγούμενα η μίξη generics με raw types μπορεί να δημιουργήσει warnings κατά την μεταγλώττιση της μορφής +
-<​code>​ +
-Note: Example.java uses unchecked or unsafe operations. +
-Note: Recompile with -Xlint:​unchecked for details. +
-</​code>​ +
-όπως παρακάτω +
-<code java WarningDemo.java>​ +
-public class WarningDemo { +
-    public static void main(String[args){ +
-        Box<​Integer>​ bi; +
-        bi = createBox();​ +
-    } +
- +
-    static Box createBox(){ +
-        return new Box(); +
-    } +
-+
-</​code>​ +
-Μεταγλωττίζοντας με το flag ''​-Xlint:​unchecked''​ εμφανίζει την παραπάνω χρήση +
-<​code>​ +
-WarningDemo.java:​4:​ warning: [uncheckedunchecked conversion +
-found   : Box +
-required: Box<​java.lang.Integer>​ +
-        bi = createBox();​ +
-                      ^ +
-1 warning +
-</​code>​ +
  
  
java/generics.1426451742.txt.gz · Last modified: 2016/02/26 11:15 (external edit)