java:interfaces
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
java:interfaces [2016/03/16 09:19] – [Στατικές μέθοδοι] gthanos | java:interfaces [2017/02/03 09:51] (current) – removed gthanos | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Διεπαφές (Interfaces) ====== | ||
- | |||
- | ===== Εισαγωγικά ===== | ||
- | |||
- | Κατά την ανάπτυξη προγραμμάτων είναι σημαντικό να είναι τυποποιημένος ο τρόπος αλληλεπίδρασης ανάμεσα σε διαφορετικά συστήματα. Η ύπαρξη ενός " | ||
- | |||
- | Υπακούοντας στην παραπάνω αρχή, η Java εισάγει την έννοια της διεπαφής (**interface**). Τα // | ||
- | |||
- | - τους προγραμματιστές που το χρησιμοποιούν και | ||
- | - τους προγραμματιστές που το υλοποιούν. | ||
- | |||
- | Οι πρώτοι γράφουν την εφαρμογή τους ώστε να χρησιμοποιεί τις μεθόδους του interface για να παρέχει συγκεκριμένη λειτουργικότητα. | ||
- | |||
- | Οι δεύτεροι, | ||
- | |||
- | Από τα παραπάνω εξάγεται ότι το interface είναι μία προσυμφωνημένη διεπαφή η οποία δεσμεύει και τις δύο πλευρές, | ||
- | |||
- | Ως παράδειγμα, | ||
- | Ένας κατασκευαστής κλιματιστικών (που θέλει να μπορεί να σταματήσει η λειτουργία της συσκευής μετά από ένα χρονικό διάστημα), | ||
- | |||
- | Οι υλοποιήσεις για τις κουζίνες και τα κλιματιστικά είναι εντελώς διαφορετικές και γίνονται ανεξάρτητα η μία από την άλλη. Η εγγύηση είναι μόνο ότι οι μέθοδοι έχουν υλοποιηθεί και παρέχουν ασφαλή και ομοιόμορφο τρόπο αλληλεπίδρασης με τα χρονόμετρα των συσκευών. | ||
- | |||
- | Ένας χρήστης αυτών των συσκευών δε χρειάζεται να γνωρίζει πώς ακριβώς λειτουργούν εσωτερικά τα χρονόμετρα, | ||
- | |||
- | Αντιπαραθέστε μια κατάσταση όπου οι συσκευές δεν τηρούν αυτό το συμβόλαιο (δηλαδή δεν υλοποιούν το συγκεκριμένο interface) και ως αποτέλεσμα ο χρήστης αναγκάζεται να χρησιμοποιήσει μια μέθοδο setACTimer για το χρονόμετρο του κλιματιστικού, | ||
- | |||
- | ===== Ορίζοντας ένα Interface ===== | ||
- | |||
- | Ένα interface ορίζεται με τρόπο παρόμοιο με κλάση, αλλά με το χαρακτηρισμό interface, για παράδειγμα, | ||
- | |||
- | <code java> | ||
- | public interface ApplianceTimer{ | ||
- | |||
- | // constant declarations | ||
- | int TIMEOUT = 20000; | ||
- | |||
- | // method signatures | ||
- | public int setTimer(int t); | ||
- | public int startTimer(int t); | ||
- | public int stopTimer(); | ||
- | } | ||
- | </ | ||
- | |||
- | Ο προσδιοριστής ενός interface είναι public ή μπορούμε να τον παραλείψουμε (package-private). | ||
- | |||
- | Ένα interface μπορεί να περιέχει **μόνο** σταθερές (πχ. το TIMΕOUT στο παράδειγμα), | ||
- | |||
- | Τόσο στις κλάσεις όσο και στα interfaces ορίζεται η ιδιότητα της κληρονομικότητας. Η διαφορά είναι ότι ενώ στην κλάση μπορούμε να έχουμε μόνο μία γονική κλάση, στα interfaces μπορούμε να έχουμε περισσότερα του ενός γονικά interfaces. | ||
- | |||
- | |||
- | ===== Υλοποιώντας ένα Interface ===== | ||
- | |||
- | Θα δείξουμε την υλοποίηση ενός interface μέσα από ένα παράδειγμα. | ||
- | |||
- | Μια πολύ συνηθισμένη χρήση interfaces είναι για το χαρακτηρισμό αντικειμένων τα οποία μπορούν να συγκριθούν μεταξύ τους: | ||
- | |||
- | Η μέθοδος '' | ||
- | <code java> | ||
- | public interface Comparable | ||
- | { | ||
- | int compareTo(Object other); | ||
- | } | ||
- | </ | ||
- | |||
- | Μια κλάση που υλοποιεί το Comparable πρέπει να υλοποιήσει τη μέθοδο '' | ||
- | |||
- | Ας δούμε ένα παράδειγμα πάνω στην κλάση Rectangle που έχουμε ορίσει σε προηγούμενες ενότητες. Σύμφωνα με το documentation του interface Comparable η compareTo πρέπει να επιστρέφει ακέραιο αρνητικό, | ||
- | |||
- | <code java Rectangle.java> | ||
- | import java.util.Arrays; | ||
- | |||
- | public class Rectangle implements Comparable { | ||
- | private int width = 0; | ||
- | private int height = 0; | ||
- | private Point origin; | ||
- | |||
- | 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; | ||
- | } | ||
- | |||
- | public void move(int x, int y) { | ||
- | origin.setX(x); | ||
- | origin.setY(y); | ||
- | } | ||
- | |||
- | public int getArea() { | ||
- | return width * height; | ||
- | } | ||
- | | ||
- | @Override | ||
- | public int compareTo(Object other) { | ||
- | Rectangle otherRect = (Rectangle) other; // απαραίτητο για να μπορεί να κληθεί η getArea | ||
- | return this.getArea() - otherRect.getArea(); | ||
- | } | ||
- | | ||
- | @Override String toString() { | ||
- | return origin + ", [" + width + " X " + height + " | ||
- | } | ||
- | | ||
- | public static void main(String args[]) { | ||
- | Rectangle [] shapes = new Rectangle[4]; | ||
- | shapes[0] = new Rectangle(4, | ||
- | shapes[1] = new Rectangle(1, | ||
- | shapes[2] = new Rectangle(2, | ||
- | shapes[3] = new Rectangle(9, | ||
- | | ||
- | System.out.println(" | ||
- | for (Rectangle rectangle : shapes) { | ||
- | System.out.println(rectangle + ", area: " + rectangle.getArea()); | ||
- | } | ||
- | | ||
- | Arrays.sort(shapes); | ||
- | | ||
- | System.out.println(" | ||
- | for (Rectangle rectangle : shapes) { | ||
- | System.out.println(rectangle + ", area: " + rectangle.getArea()); | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | Για να εκτελέσετε το παράδειγμα κατεβάστε την κλάση '' | ||
- | |||
- | ===== Χρησιμοποιώντας ένα interface ως τύπο δεδομένων ===== | ||
- | |||
- | Μπορείτε να χρησιμοποιήσετε ένα Java Interface ως ένα reference τύπο δεδομένων. Μπορείτε να χρησιμοποιήσετε το όνομα ενός interface ως τον τύπο μιας παραμέτρου σε μία Java μέθοδο ή τύπο μιας τοπικής μεταβλητής στο σώμα μίας μεθόδου. Προϋπόθεση είναι οι τιμές των μεταβλητών να δείχνουν σε αντικείμενα των οποίων οι κλάσεις υλοποιούν το συγκεκριμένο interface. Δείτε το παρακάτω παράδειγμα. | ||
- | |||
- | Αρχικά ορίζουμε ένα interface με όνομα MyComparable το οποίο ορίζει τη μέθοδο isLarger που επιστρέφει true ή false ανάλογα με το αν το τρέχον αντικείμενο είναι μεγαλύτερο ή μικρότερο της παραμέτρου : | ||
- | |||
- | <code java MyComparable.java> | ||
- | public interface MyComparable { | ||
- | public boolean isLarger(MyComparable other); | ||
- | } | ||
- | </ | ||
- | |||
- | Γράφουμε την 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, | ||
- | } | ||
- | | ||
- | public Rectangle(int initWidth, int initHeight) { | ||
- | this(initWidth, | ||
- | } | ||
- | |||
- | 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() { | ||
- | | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | Τώρα, μπορούμε να ορίσουμε την κλάση '' | ||
- | |||
- | <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; | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | Χρησιμοποιούμε την TwosComparator ως εξής: | ||
- | <code java Main.java> | ||
- | public class Main { | ||
- | |||
- | public static void main(String args[]) { | ||
- | Rectangle rec1 = new Rectangle(30, | ||
- | Rectangle rec2 = new Rectangle(10, | ||
- | TwosComparator comp = new TwosComparator(); | ||
- | |||
- | System.out.println(" | ||
- | System.out.println(" | ||
- | |||
- | System.out.println( comp.findLargest(rec1, | ||
- | System.out.println( comp.findSmallest(rec1, | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | ===== Μεταβάλλοντας ένα υφιστάμενο Interface ===== | ||
- | |||
- | Κάποιες φορές στον προγραμματισμό εμφανίζεται η ανάγκη να μεταβάλλουμε ένα υφιστάμενο interface ή να το επεκτείνουμε. Το πρόβλημα σε αυτές τις περιπτώσεις είναι ότι οι κλάσεις που υλοποιούν το συγκεκριμένο interface με την προσθήκη ή αλλαγή των υφιστάμενων μεθόδων θα πάψουν να το υλοποιούν. Επομένως μία μονομερής αλλαγή του interface δεν είναι εφικτή. Παρόλα αυτά υπάρχουν δύο εναλλακτικές που μπορούν να μας καλύψουν. | ||
- | |||
- | |||
- | **1η εναλλακτική: | ||
- | <code java> | ||
- | public interface DoIt { | ||
- | |||
- | void doSomething(int i, double x); | ||
- | int doSomethingElse(String s); | ||
- | } | ||
- | |||
- | public interface DoItPlus extends DoIt { | ||
- | |||
- | | ||
- | |||
- | } | ||
- | </ | ||
- | |||
- | **2η εναλλακτική: | ||
- | <code java> | ||
- | public interface DoIt { | ||
- | |||
- | void doSomething(int i, double x); | ||
- | int doSomethingElse(String s); | ||
- | | ||
- | // Method body | ||
- | | ||
- | } | ||
- | </ | ||
- | |||
- | ===== Default μέθοδοι ===== | ||
- | |||
- | <WRAP 80% important center round> | ||
- | **__Παρατήρηση: | ||
- | </ | ||
- | |||
- | Ας θεωρήσουμε το παρακάτω υποθετικό interface | ||
- | |||
- | <code java StringInverter.java> | ||
- | public interface StringInverter { | ||
- | /** inverts a string | ||
- | */ | ||
- | String invert(String str); | ||
- | } | ||
- | </ | ||
- | |||
- | Ας υποθέσουμε τώρα ότι η κλάση 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(); | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | Αν υποθέσουμε ότι το αρχικό 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); | ||
- | } | ||
- | </ | ||
- | |||
- | Τώρα η κλάση 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(); | ||
- | | ||
- | | ||
- | | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ===== Στατικές μέθοδοι ===== | ||
- | |||
- | Εκτός από //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(); | ||
- | | ||
- | | ||
- | | ||
- | } | ||
- | | ||
- | /** 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, | ||
- | String str2; | ||
- | if( str.length() >= index-1 ) { | ||
- | str2 = new String("" | ||
- | } | ||
- | else { | ||
- | str2 = str.substring(index+1); | ||
- | } | ||
- | return str1+str2; | ||
- | | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | <WRAP 80% important center round> | ||
- | **__Παρατήρηση: | ||
- | </ | ||
- | |||
- | |||
- | | Προηγούμενο : [[ :java:ant | Αυτόματη μεταγλώττιση με χρήση Apache Ant ]] | [[ :toc | Περιεχόμενα ]] | Επόμενο: | ||
- | |||
- | |||
java/interfaces.1458119976.txt.gz · Last modified: 2016/03/16 09:19 by gthanos