java:interfaces

This is an old revision of the document!


A PCRE internal error occured. This might be caused by a faulty plugin

====== Διεπαφές (Interfaces) ====== ===== Εισαγωγικά ===== Κατά την ανάπτυξη προγραμμάτων, υπάρχουν περιπτώσεις που είναι σημαντικό να διαχωρίσουμε την δουλειά μεταξύ προγραμματιστών, ώστε κάθε ομάδα προγραμματιστών να μπορεί να αναπτύξει κώδικα ανεξάρτητα από τις υπόλοιπες ομάδες. Φανταστείτε το πληροφοριακό σύστημα μιας Τράπεζας. Υπάρχει μία ομάδα προγραμματιστών που ασχολείται με τη διαχείριση των δεδομένων της Τράπεζας (χρήστες, πελάτες, λογαριασμοί πελατών κλπ), μία ομάδα που ασχολείται με το εσωτερικό πληροφοριακό σύστημα της τράπεζας (υπηρεσίες προς τους υπαλλήλους της) και μία άλλη ομάδα που ασχολείται με το e-banking και m-banking (υπηρεσίες προς τους πελάτες της). Οι ομάδες προγραμματιστών που ασχολούνται με υπηρεσίες θέλουν με κάποιον τρόπο να έχουν ασφαλή πρόσβαση στα δεδομένα της τράπεζας. Για τον λόγο αυτό, η ομάδα που ασχολείται με την διαχείριση των δεδομένων της Τράπεζας δίνει μια σειρά από μεθόδους μέσω των οποίων, οι άλλες ομάδες μπορούν να έχουν ασφαλή και πιστοποιημένη πρόσβαση στα δεδομένα αυτά. Στην γλώσσα του προγραμματισμού, λέμε ότι η ομάδα που είναι υπεύθυνη για την διαχείριση των δεδομένων παρέχει ένα //Application Programming Interface (**API**)// προς τις άλλες ομάδες. Οι άλλες ομάδες δεν ενδιαφέρονται για το πως έχει υλοποιηθεί το παραπάνω //API//, αλλά θέλουν να το χρησιμοποιήσουν με ασφάλεια. Η ομάδα που διαχειρίζεται τα δεδομένα, μπορεί να αλλάξει την εσωτερική υλοποίηση των παραπάνω μεθόδων που αποτελούν το συγκεκριμένο //API//, αρκεί να μην το μεταβάλει. Από τα παραπάνω συμπεραίνουμε ότι κάθε //API// λειτουργεί σαν **"συμβόλαιο"** μεταξύ δύο ομάδων προγραμματιστών, αυτούς που το υλοποιούν και τους χρήστες του. ===== Τα Java Interfaces ===== Υπακούοντας στην παραπάνω αρχή, η Java εισάγει την έννοια της διεπαφής (**interface**). Τα //interfaces// στη Java λειτουργούν ως προ-συμφωνημένες διεπαφές μεταξύ προγραμματιστών. Κάθε Interface έχει κατά κανόνα δύο μέρη - τους προγραμματιστές που το υλοποιούν και - τους προγραμματιστές που το χρησιμοποιούν. Οι δεύτεροι εάν θέλουν όμως να επωφεληθούν από την λειτουργικότητα που υποδεικνύει ένα //interface// θα πρέπει να χρησιμοποιούν τις μεθόδους του στα προγράμματά τους και να επιλέγουν προς χρήση κλάσεις που υλοποιούν το συγκεκριμένο interface. Με αυτόν τον τρόπο προσπαθούμε να γράψουμε κώδικα που είναι αρκετά γενικός, δηλαδή δεν στηρίζεται απαραίτητα σε συγκεκριμένες κλάσεις, αλλά σε ομάδες κλάσεων που έχουν ως κοινό χαρακτηριστικό την υλοποίηση του συγκεκριμένου //interface//. Αντίστοιχα, οι προγραμματιστές που υλοποιούν μία λειτουργία είναι πολύ πιθανό να θέλουν να υλοποιήσουν ένα ή περισσότερα //interfaces//, έτσι ώστε προγράμματα που χρησιμοποιούν τα //interfaces// αυτά να είναι συμβατά με τον κώδικα που φτιάχνουν. Από τα παραπάνω εξάγεται ότι το interface είναι μία προσυμφωνημένη διεπαφή η οποία δεσμεύει και τις δύο πλευρές, δηλαδή οι χρήστες του δεσμεύονται να χρησιμοποιούν τις μεθόδους του //interface// και οι προγραμματιστές που το υλοποιούν να το υλοποιούν στις κλάσεις τους. Παρακάτω θα δούμε την υποστήριξη των interfaces από τον compiler της Java και τους κανόνες που τα διέπουν. ===== Ορίζοντας ένα Interface ===== Παρακάτω δίνεται το παράδειγμα ορισμού ενός Interface ως εξής: <code java> public interface MyInterface extends Interface1, Interface2, Interface3 { // constant declarations // base of natural logarithms double E = 2.718282; double PI = 3.14159; // method signatures public void interfaceMethod(int i, double x); public int interfaceMethod2(String s); } </code> * **public ή κανένας προσδιοριστής πρόσβασης:** Αν οριστεί //public// τότε το interface είναι ορατό από όλες τις κλάσεις και όλα τα πακέτα στην Java. Αν δεν οριστεί κάτι (package private) τότε το interface είναι ορατό μόνο μέσα στο πακέτο στο οποίο δηλώνεται. * **interface:** δεσμευμένη λέξη * **το όνομα του interface:** στο παράδειγμα μας ''MyInterface''. * **extends:** ένα ή περισσότερα interfaces τα οποία επεκτείνει το συγκεκριμένο interface (comma seperated). Τόσο στις κλάσεις όσο και στα interfaces ορίζεται η ιδιότητα της κληρονομικότητας. Η διαφορά είναι ότι ενώ στην κλάση μπορούμε να έχουμε μόνο μία γονική κλάση, στα interfaces μπορούμε να έχουμε περισσότερα του ενός γονικά interfaces. ==== Το σώμα του interface ==== Ένα interface μπορεί να περιέχει πεδία όπως στο παραπάνω παράδειγμα. <code java> double E = 2.718282; double PI = 3.14159; </code> Τα πεδία αυτά εξ ορισμού (by default) ''public, static, final'', δηλ είναι **__σταθερές__** που ανήκουν στις κλάσεις που θα υλοποιήσουν το interface στο οποίο δηλώνονται. Οι μέθοδοι σε ένα interface μπορούν να ανήκουν στις παρακάτω 3 κατηγορίες a) abstract methods, b) default methods, static methods. Θα εξηγήσουμε κάθε μία από τις 3 κατηγορίες παρακάτω. Όλες οι μέθοδοι σε ένα interface είναι εξ ορισμού //public//, κατά συνέπεια ο προσδιορισστής public μπορεί να παραληφθεί δηλ <code java> public interface MyInterface extends Interface1, Interface2, Interface3 { // constant declarations // base of natural logarithms double E = 2.718282; double PI = 3.14159; // method signatures public void interfaceMethod(int i, double x); public int interfaceMethod2(String s); } </code> ===== Υλοποιώντας ένα Interface ===== Ας υποθέσουμε ότι για τις ανάγκες αυτής της παραγράφου ορίζουμε ένα interface το οποίο ορίζει ένα τρόπο για να συγκρίνουμε το μέγεθος επιμέρους αντικειμένων. <code java StrictlyComparable.java> public interface StrictlyComparable { // this (object calling isLargerThan) // and other must be instances of // the same class returns 1, 0, -1 // if this is greater than, // equal to, or less than other public int isLarger(StrictlyComparable other); } </code> Ας υποθέσουμε ότι θέλουμε να συγκρίνουμε το μέγεθος αντικειμένων του ιδίου τύπου (ανεξάρτητα με το τι αντικείμενα είναι αυτά), οι κλάσεις που υλοποιούν τα αντικείμενα αυτά μπορούν να υλοποιούν το interface ''StrictlyComparable''. Για παράδειγμα, εάν πρόκειται για Strings μπορούμε να συγκρίνουμε το μέγεθος του String, αν πρόκειται για δι-διάστατα σχήματα θα μπορούσαμε να συγκρίνουμε το εμβαδό τους, ενώ αν πρόκειται για τρισδιάστατα σχήματα θα μπορούσαμε να συγκρίνουμε τον όγκο τους. Ας υποθέσουμε ότι υλοποιούμε την κλάση Rectangle όπως αυτή δίνεται παρακάτω: <code java Rectangle.java> public class Rectangle implements StrictlyComparable { public int width = 0; public int height = 0; public Point origin; // four constructors public Rectangle() { origin = new Point(0, 0); } public Rectangle(Point p) { origin = p; } public Rectangle(int w, int h) { origin = new Point(0, 0); width = w; height = h; } public Rectangle(Point p, int w, int h) { origin = p; width = w; height = h; } // a method for moving the rectangle public void move(int x, int y) { origin.setX(x); origin.setY(y); } // a method for computing // the area of the rectangle public int getArea() { return width * height; } // a method required to implement // the Relatable interface public int isLarger(StrictlyComparable other) { Rectangle otherRect = (Rectangle)other; if (this.getArea() < otherRect.getArea()) return -1; else if (this.getArea() > otherRect.getArea()) return 1; else return 0; } public static void main(String args[]) { Point p = new Point(10,20); Rectangle rec1 = new Rectangle(p, 30, 40); Rectangle rec2 = new Rectangle(p, 30, 20); if( rec1.isLarger(rec2) > 0 ) { System.out.println("rec1 is larger!"); } else if( rec1.isLarger(rec2) < 0 ) { System.out.println("rec2 is larger!"); } else { System.out.println("req1 is equal to req2"); } } } </code> **Σημείωση:** Αν παραλείψετε την γραμμή <code java> Rectangle otherRect = (Rectangle)other; </code> ο compiler δεν γνωρίζει ότι η μεταβλητή ''other'' ανήκει στην κλάση ''Rectangle'' και η προσπάθεια μεταγλώτισσης θα εμφανίσει λάθος (δοκιμάστε το!). Για να εκτελέσετε το παράδειγμα κατεβάστε την κλάση ''Point'', από [[:java:objects |εδώ]]. ===== Χρησιμοποιώντας ένα interface ως τύπο δεδομένων ===== Μπορείτε να χρησιμοποιήσετε ένα Java Interface ως ένα reference τύπο δεδομένων. Μπορείτε να χρησιμοποιήσετε το όνομα ενός interface ως τον τύπο μιας παραμέτρου σε μία Java μέθοδο ή τύπο μιας τοπικής μεταβλητής στο σώμα μίας μεθόδου. Προϋπόθεση είναι οι τιμές των μεταβλητών να δείχνουν σε αντικείμενα των οποίων οι κλάσεις υλοποιούν το συγκεκριμένο interface. Δείτε το παρακάτω παράδειγμα. <code java FinestComparator.java> public class FinestComparator { /* Παράδειγμα χρήσης interface ως τύπο τοπικής μεταβλητής στο σώμα μιας μεθόδου. */ public Object findLargest(Object object1, Object object2) { StrictlyComparable obj1 = (StrictlyComparable)object1; StrictlyComparable obj2 = (StrictlyComparable)object2; if ((obj1).isLarger(obj2) > 0) return object1; else return object2; } /* Παράδειγμα χρήσης interface ως τύπο τοπικής μεταβλητής στο σώμα μιας μεθόδου. */ public Object findSmallest(Object object1, Object object2) { StrictlyComparable obj1 = (StrictlyComparable)object1; StrictlyComparable obj2 = (StrictlyComparable)object2; if ((obj1).isLarger(obj2) < 0) return object1; else return object2; } /* η μέθοδος isEqual δίνεται εδώ με δύο διαφορετικές εκδοχές * οι οποίες είναι ισοδύναμες. */ public boolean isEqual(Object object1, Object object2) { StrictlyComparable obj1 = (StrictlyComparable)object1; StrictlyComparable obj2 = (StrictlyComparable)object2; if ( (obj1).isLarger(obj2) == 0) return true; else return false; } /* Παράδειγμα χρήσης interface ως τύπο τυπικής παραμέτρου σε μία μέθοδο. */ public boolean isEqual(StrictlyComparable object1, StrictlyComparable object2) { if ( (object1).isLarger(object2) == 0) return true; else return false; } public static void main(String args[]) { Point p = new Point(10,20); Rectangle rec1 = new Rectangle(p, 30, 40); Rectangle rec2 = new Rectangle(p, 30, 40); FinestComparator comp = new FinestComparator(); System.out.println("rec1 is "+rec1.toString()); System.out.println("rec2 is "+rec2.toString()); if( !comp.isEqual(rec1,rec2) ) { System.out.println( comp.findLargest(rec1, rec2).toString()+" is larger!"); System.out.println( comp.findSmallest(rec1, rec2).toString()+" is smaller!"); } else { System.out.println("Objects are equal!"); } } } </code> Οι παραπάνω μέθοδοι δουλεύουν για οποιοδήποτε αντικείμενο η κλάση του υλοποιεί το παραπάνω interface, ώστε να μπορεί να κληθεί εσωτερικά η μέθοδος ''isLarger()''. Παρατηρήστε τις δύο διαφορετικές αλλά ισοδύναμες εκδοχές της μεθόδου ''isEqual''. ===== Μεταβάλλοντας ένα υφιστάμενο Interface ===== Κάποιες φορές στον προγραμματισμό εμφανίζεται η ανάγκη να μεταβάλλουμε ένα υφιστάμενο interface ή να το επεκτείνουμε. Το πρόβλημα σε αυτές τις περιπτώσεις είναι ότι οι κλάσεις που υλοποιούν το συγκεκριμένο interface με την προσθήκη ή αλλαγή των υφιστάμενων μεθόδων θα πάψουν να το υλοποιούν. Επομένως μία μονομερής αλλαγή του interface δεν είναι εφικτή. Παρόλα αυτά υπάρχουν δύο εναλλακτικές που μπορούν να μας καλύψουν. **1η εναλλακτική:** Να υλοποιήσουμε ένα νέο interface με παραπλήσιο όνομα, το οποίο να αποτελέσει τη νέα έκδοση του υφιστάμενου interface. <code java> public interface DoItPlus extends DoIt { boolean didItWork(int i, double x, String s); } </code> **2η εναλλακτική:** Να υλοποιήσουμε μία ή περισσότερες //default// μέθοδους (όσες είναι και οι αλλαγές ή οι προσθήκες) μέσα στο υφιστάμενο interface, ώστε ακόμη και εάν οι κλάσεις που υλοποιούν το συγκεκριμένο interface δεν υλοποιούν τις νέες μεθόδους, οι υλοποιήσεις στις κλάσεις αυτές να γίνονται από το ίδιο το interface. <code java> public interface DoIt { void doSomething(int i, double x); int doSomethingElse(String s); default boolean didItWork(int i, double x, String s) { // Method body } } </code> ===== Default μέθοδοι ===== <WRAP 80% important center round> **__Παρατήρηση:__** Οι **default** μέθοδοι στην Java υλοποιούνται στην έκδοση 8 της γλώσσας. Μεταγλωττιστές που ανήκουν σε προηγούμενες εκδόσεις αδυνατούν να μεταγλωττίσουν τον παρακάτω κώδικά. </WRAP> Ας θεωρήσουμε το παρακάτω υποθετικό interface <code java StringInverter.java> public interface StringInverter { /** inverts a string */ String invert(String str); } </code> Ας υποθέσουμε τώρα ότι η κλάση MyString υλοποιεί το παραπάνω interface. <code java MyString.java> import java.lang.*; public class MyString implements StringInverter { String invert(String str) { StringBuffer strb = new StringBuffer(str); return strb.reverse().toString(); } } </code> Αν υποθέσουμε ότι το αρχικό interface αλλάζει στο παρακάτω <code java StringInverter.java> public interface StringInverter { /** inverts a string */ String invert(String str); /** splits a string in half and inverts * the two parts. */ String invertHalf(String str); } </code> Τώρα η κλάση MyString δεν υλοποιεί πλέον το αρχικό interface. Αυτό μπορεί να συνεχίσει να υλοποιείται εάν η νέα μέθοδος έχει μία default υλοποίηση μέσα στο interface όπως παρακάτω. <code java StringInverter.java> public interface StringInverter { /* inverts a string */ String invert(String str); /* splits a string in half and inverts * the two parts */ default String invertHalf(String str) { int len = str.length(); String str1 = str.substring(0, len/2); String str2 = str.substring(len/2, len); return str2+str1; } } </code> ===== Στατικές μέθοδοι ===== Εκτός από //default// μεθόδους ένα interface μπορεί να περιέχει και στατικές (//static//) μεθόδους. Οι στατικές μέθοδοι ορίζονται σε αναλογία με τις στατικές μεθόδους των κλάσεων και ουσιαστικά ανήκουν στις κλάσεις που θα υλοποιήσουν το interface και όχι στα αντικείμενα. Ένα παράδειγμα μπορείτε να δείτε παρακάτω. <code java StringInverter.java> public interface StringInverter { /** inverts a string */ String invert(String str); /** splits a string in half and inverts * the two parts */ default String invertHalf(String str) { int len = str.length(); String str1 = str.substring(0, len/2); String str2 = str.substring(len/2, len); return str2+str1; } /** removes character at index from String str * and returns the new String. */ static String removeChar(String str, int index) { if( str.length()-1 < index ) { return str; } String str1 = str.substring(0,index); String str2; if( str.length() >= index-1 ) { str2 = new String(""); } else { str2 = str.substring(index+1); } return str1+str2; } } </code>

java/interfaces.1456485334.txt.gz · Last modified: 2016/02/26 16:33 (external edit)