User Tools

Site Tools


java:interfaces

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
java:interfaces [2017/01/31 14:35] – [Υλοποιώντας ένα Interface] gthanosjava:interfaces [2017/02/03 09:51] (current) – removed gthanos
Line 1: Line 1:
-====== Διεπαφές (Interfaces) ====== 
- 
-===== Εισαγωγικά ===== 
- 
-Κατά την ανάπτυξη προγραμμάτων είναι σημαντικό να είναι τυποποιημένος ο τρόπος αλληλεπίδρασης ανάμεσα σε διαφορετικά συστήματα. Η ύπαρξη ενός "συμβολαίου" το οποίο καθορίζει ακριβώς το πώς μπορεί μια εφαρμογή να αλληλεπιδράσει με μια άλλη διευκολύνει την ανεξάρτητη ανάπτυξη κώδικα από διαφορετικές ομάδες. 
- 
-Υπακούοντας στην παραπάνω αρχή, η Java εισάγει την έννοια της διεπαφής (**interface**). Τα //interfaces// στη Java λειτουργούν ως προ-συμφωνημένες διεπαφές μεταξύ προγραμματιστών. Κάθε interface τυπικά σχετίζεται με: 
-  - τους προγραμματιστές που το χρησιμοποιούν και  
-  - τους προγραμματιστές που το υλοποιούν.  
- 
-Οι πρώτοι γράφουν την εφαρμογή τους ώστε να χρησιμοποιεί τις μεθόδους του interface για να παρέχει συγκεκριμένη λειτουργικότητα.  
- 
-Οι δεύτεροι, αν θέλουν να επωφεληθούν από την λειτουργικότητα που υποδεικνύει ένα //interface//  θα πρέπει να υλοποιήσουν τις μεθόδους του στα προγράμματά τους και να επιλέγουν προς χρήση κλάσεις που υλοποιούν το συγκεκριμένο interface.  
- 
-Από τα παραπάνω εξάγεται ότι το //interface// είναι μία προσυμφωνημένη διεπαφή η οποία δεσμεύει και τις δύο πλευρές, δηλαδή οι χρήστες του δεσμεύονται να χρησιμοποιούν τις μεθόδους του //interface// και οι προγραμματιστές που το υλοποιούν να το υλοποιούν στις κλάσεις τους. Παρακάτω θα δούμε την υποστήριξη των interfaces από τον compiler της Java και τους κανόνες που τα διέπουν. 
- 
-Ως παράδειγμα, θεωρήστε ένα //interface// το οποίο ορίζει τη χρήση ενός χρονόμετρου. Όσοι επιθυμούν να υλοποιήσουν το χρονόμετρο θα πρέπει κατ' ελάχιστο να υλοποιήσουν τις παρακάτω μεθόδους: 
-  * **setTimer:** αρχικοποίηση του timer  
-  * **startTimer:** έναρξη του timer και  
-  * **endTimer:** τερματισμός του timer 
- 
-Ας υποθέσουμε τώρα ότι ένας κατασκευαστής ηλεκτρικών συσκευών (κλιμαστιστικά, ψυγεία κ.α.) που χρησιμοποιεί Java για τον αυτοματισμό τους, θέλει να χρησιμοποιεί μία κλάση τύπου timer για τις συσκευές που κατασκευάζει. Μπορεί να χρησιμοποιήσει οποιαδήποτε κλάση που είναι σύμφωνη με τον παρακάτω interface, ανεξάρτητα από την προέλευση της. Γνωρίζοντας ότι όλες κλάσεις τύπου timer που χρησιμοποιεί υλοποιούν το συγκεκριμένο interface, αρκεί να σχεδιάσει και να υλοποιήσει τα προϊόντα του ώστε να χρησιμοποιούν το  interface αυτό.  
- 
-Οποιοσδήποτε προγραμματιστής χρησιμοποιεί το παραπάνω //interface// δεν χρειάζεται να γνωρίζει το παραμικρό για την εσωτερική υλοποίηση του timer. Η τελική επιλογή της κλάσης του timer μπορεί να μεταβάλλεται χωρίς να επηρεάζεται η υπόλοιπη υλοποίηση για την κάθε συσκευή. 
- 
-Αντιπαραθέστε μια κατάσταση όπου οι συσκευές δεν τηρούν αυτό το συμβόλαιο. Σε αυτή την περίπτωση κάθε συσκευή θα έπρεπε να υλοποιεί τον δικό της timer. Η προτυποποίηση που παρέχει ο μηχανισμός του //interface// είναι αυτή που επιτρέπει: 
-  - την εύκολη και χωρίς σφάλματα επαναχρησιμοποίηση κώδικα σε διαφορετικά προγράμματα ή  
-  - την αλλαγή της υφιστάμενης υλοποίησης μιας κλάσης, δίχως οι χρήστες της να έχουν γνώση για τις αλλαγές αυτές. 
- 
-===== Ορίζοντας ένα Interface ===== 
- 
-Ένα //interface// μπορεί να περιέχει πεδία όπως στο παραπάνω παράδειγμα. 
- 
-<code java> 
-double E = 2.718282; 
-double PI = 3.14159; 
-</code> 
- 
-Τα πεδία αυτά εξ ορισμού (by default) ''public, static, final'', δηλ είναι **__σταθερές__** που ανήκουν στις κλάσεις που θα υλοποιήσουν το interface στο οποίο δηλώνονται. 
- 
-Οι μέθοδοι σε ένα interface κατά κανόνα είναι **abstract**, δηλαδή περιέχεται μόνο η δήλωση της μεθόδου χωρίς σώμα (δηλ. υλοποίηση). Επίσης, όλες οι μέθοδοι σε ένα 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 interfaceMethod1(int i, double x); 
-    public int interfaceMethod2(String s); 
-} 
-</code> 
- 
-===== Χρησιμοποιώντας ένα interface ως τύπο δεδομένων ===== 
- 
-Ας υποθέσουμε τώρα ότι θέλουμε να χρησιμοποιήσουμε μία στοίβα για να αντιμεταθέσουμε τα στοιχεία ενός πίνακα χαρακτήρων. Ο πίνακας αρχικά περιέχει την αγγλική αλφαβήτα (26 χαρακτήρες) και θέλουμε να αντιστρέψουμε την σειρά με την οποία αποθηκεύονται οι χαρακτήρες στον πίνακα. Η μέθοδος invertArray παρακάτω αντιμεταθέτει τα στοιχεία του πίνακα array με χρήση της στοίβας stk. 
- 
-<code java ArrayManipulator.java> 
-public class ArrayManipulator { 
-  public static void main(String []args) { 
-    Character alphabet[] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; 
-    printCharacterArray(alphabet); 
-    System.out.println("-----------"); 
-     
-    Stack stack = new ArrayStack(); 
-    invertArray(alphabet, stack); 
-     
-    printCharacterArray(alphabet); 
-  } 
-   
-  public static void invertArray(Object []array, Stack stk) { 
-    for(int i=0; i<array.length; i++) 
-      stk.push(array[i]); 
-    for(int i=0; i<array.length; i++) 
-      array[i] = stk.pop(); 
-  } 
-   
-  public static void printCharacterArray(Character []array) { 
-    for(Character c : array) 
-      System.out.print(c+"   "); 
-    System.out.println(""); 
-  } 
-} 
-</code> 
- 
-<WRAP important 80% center round> 
-Παρατηρήστε ότι η μέθοδος //invertArray// παίρνει ως 2ο όρισμα όχι το αντικείμενο μίας κλάσης αλλά ένα αντικείμενο τύπου interface //Stack//! Στην πραγματικότητα αυτό που δηλώνει η συγκεκριμένη μέθοδος είναι ότι ως 2ο όρισμα παίρνει ένα αντικείμενο του οποίου η κλάση υλοποιεί το interface //Stack//. Επίσης, δηλώνει ότι από όλες τις διαθέσιμες μεθόδους της κλάσης μπορούν να χρησιμοποιηθούν μόνο οι μέθοδοι που δηλώνονται στο interface. 
-</WRAP> 
- 
-<WRAP todo 80% center round> 
-Στο παραπάνω πρόγραμμα, αλλάξτε την γραμμή 
-<code java> 
-Stack stack = new ArrayStack(); 
-</code> 
-σε 
-<code java> 
-Stack stack = new LinkedStack(); 
-</code> 
- 
-Παρατηρήστε αν σας βγάζει κάποιο μήνυμα ο //compiler// και εάν εκτελώντας το πρόγραμμα παράγεται το ίδιο τελικό αποτέλεσμα. Τι συμπέρασμα βγάζετε για τη χρήση της στοίβας από την μέθοδο //invertArray()//; 
-</WRAP> 
- 
-===== Χρησιμοποιώντας ένα interface ως τύπο δεδομένων ===== 
- 
-Μπορείτε να χρησιμοποιήσετε ένα Java Interface ως ένα reference τύπο δεδομένων. Μπορείτε να χρησιμοποιήσετε το όνομα ενός interface ως τον τύπο μιας παραμέτρου σε μία Java μέθοδο ή τύπο μιας τοπικής μεταβλητής στο σώμα μίας μεθόδου. Προϋπόθεση είναι οι τιμές των μεταβλητών να δείχνουν σε αντικείμενα των οποίων οι κλάσεις υλοποιούν το συγκεκριμένο interface. Δείτε το παρακάτω παράδειγμα. 
- 
-Αρχικά ορίζουμε ένα interface με όνομα MyComparable το οποίο ορίζει τη μέθοδο isLarger που επιστρέφει true ή false ανάλογα με το αν το τρέχον αντικείμενο είναι μεγαλύτερο ή μικρότερο της παραμέτρου :  
- 
-<code java MyComparable.java>  
-public interface MyComparable { 
-  public boolean isLarger(MyComparable other); 
-} 
-</code> 
- 
-Γράφουμε την Rectangle ώστε να υλοποιεί και το νέο μας interface: 
-<code java Rectangle.java> 
-public class Rectangle implements Comparable, MyComparable{ 
-     
-  // the Rectangle class has 3 fields 
-  private int width; 
-  private int height; 
-  private Point origin; 
-     
-  // the Rectangle class has one constructor 
-  public Rectangle(int initWidth, int initHeight, Point initOrigin) { 
-    width = initWidth; 
-    height = initHeight; 
-    origin = initOrigin; 
-  } 
-   
-  public Rectangle(int initWidth, int initHeight, int xPos, int yPos) { 
-    this(initWidth, initHeight, new Point(xPos,yPos)); 
-  } 
-   
-  public Rectangle(int initWidth, int initHeight) { 
-    this(initWidth, initHeight, 0, 0); 
-  } 
- 
-  public void setWidth(int newWidth ) { 
-    width = newWidth; 
-  } 
-   
-  public int getWidth() { return width; } 
-     
-  public void setHeight(int newHeight ) { 
-    height = newHeight; 
-  } 
-   
-  public int getHeight() { return height; } 
-     
-  public void setOrigin(Point newOrigin) { 
-    origin = newOrigin; 
-  } 
-   
-  public Point getOrigin() { return origin; } 
-   
-  @Override 
-  public int compareTo(Object other) { 
-    Rectangle otherRect = (Rectangle) other; 
-    return (this.getArea() - otherRect.getArea()); 
-  } 
-  
-  @Override 
-  public boolean isLarger(MyComparable other) { 
-   Rectangle otherRect = (Rectangle) other; 
-    return (this.getArea() > otherRect.getArea()); 
-  } 
-  
-  @Override 
-  public String toString() { 
-    return origin + ", Dimensions [" + width + " X " + height + "]"; 
-  } 
-  
-  public int getArea() { 
-       return width * height; 
-  } 
- 
-</code> 
- 
-Τώρα, μπορούμε να ορίσουμε την κλάση ''TwosComparator'' η οποία παρέχει μεθόδους που μπορούν να χρησιμοποιηθούν για σύγκριση ανάμεσα σε οποιαδήποτε δύο αντικείμενα //εφόσον// αυτά υλοποιούν το MyComparable.  
- 
-<code java TwosComparator.java> 
-public class TwosComparator { 
-     
-    public MyComparable findLargest(MyComparable object1, MyComparable object2) { 
-        if (object1.isLarger(object2)) 
-            return object1; 
-        else  
-            return object2; 
-    } 
-     
-    public MyComparable findSmallest(MyComparable object1, MyComparable object2) { 
-        if (!(object1.isLarger(object2))) 
-            return object1; 
-        else  
-            return object2; 
-    } 
-} 
-</code> 
- 
-Χρησιμοποιούμε την TwosComparator ως εξής:  
-<code java Main.java> 
-public class Main { 
-       
-    public static void main(String args[]) { 
-        Rectangle rec1 = new Rectangle(30, 40); 
-        Rectangle rec2 = new Rectangle(10, 50); 
-        TwosComparator comp = new TwosComparator(); 
-      
-        System.out.println("rec1 is " + rec1); 
-        System.out.println("rec2 is " + rec2); 
-      
-        System.out.println( comp.findLargest(rec1, rec2) + "  is larger!"); 
-        System.out.println( comp.findSmallest(rec1, rec2) + "  is smaller!"); 
-    } 
-} 
-</code> 
- 
-===== Μεταβάλλοντας ένα υφιστάμενο Interface ===== 
- 
-Κάποιες φορές στον προγραμματισμό εμφανίζεται η ανάγκη να μεταβάλλουμε ένα υφιστάμενο interface ή να το επεκτείνουμε. Το πρόβλημα σε αυτές τις περιπτώσεις είναι ότι οι κλάσεις που υλοποιούν το συγκεκριμένο interface με την προσθήκη ή αλλαγή των υφιστάμενων μεθόδων θα πάψουν να το υλοποιούν. Επομένως μία μονομερής αλλαγή του interface δεν είναι εφικτή. Παρόλα αυτά υπάρχουν δύο εναλλακτικές που μπορούν να μας καλύψουν. 
- 
-  
-**1η εναλλακτική:** Να υλοποιήσουμε ένα νέο interface με παραπλήσιο όνομα, το οποίο να αποτελέσει τη νέα έκδοση του υφιστάμενου interface. 
-<code java> 
-public interface DoIt { 
-  
-   void doSomething(int i, double x); 
-   int doSomethingElse(String s); 
-} 
- 
-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> 
- 
-<WRAP 80% important center round> 
-**__Παρατήρηση:__** Οι **static** μέθοδοι στην Java υλοποιούνται στην έκδοση 8 της γλώσσας. Μεταγλωττιστές που ανήκουν σε προηγούμενες εκδόσεις αδυνατούν να μεταγλωττίσουν τον παραπάνω κώδικά. 
-</WRAP> 
- 
- 
-| Προηγούμενο : [[ :java:ant | Αυτόματη μεταγλώττιση με χρήση Apache Ant ]] | [[ :toc | Περιεχόμενα ]] | Επόμενο: [[ :java:access_modifiers | Περιοριστές πρόσβασης ]]  | 
- 
- 
  
java/interfaces.1485873326.txt.gz · Last modified: 2017/01/31 14:35 by gthanos