This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
java:interfaces [2017/02/03 09:18] gthanos [Υλοποιώντας το Interface] |
java:interfaces [2017/02/03 09:51] gthanos removed |
||
|---|---|---|---|
| Line 30: | Line 30: | ||
| - την αλλαγή της υφιστάμενης υλοποίησης μιας κλάσης που υλοποιεί ένα // | - την αλλαγή της υφιστάμενης υλοποίησης μιας κλάσης που υλοποιεί ένα // | ||
| - | ===== Ορίζοντας ένα Interface ===== | + | | Προηγούμενο : [[ :java:abstract_classes | Abstract |
| - | + | ||
| - | Παρακάτω δίνεται το παράδειγμα ορισμού ενός 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); | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | * **public ή κανένας προσδιοριστής πρόσβασης:** Αν οριστεί //public// τότε το interface είναι ορατό από όλες τις κλάσεις και όλα τα πακέτα στην Java. Αν δεν οριστεί κάτι (package private) τότε το interface είναι ορατό μόνο μέσα στο πακέτο στο οποίο δηλώνεται. | + | |
| - | * **interface: | + | |
| - | * **το όνομα του interface: | + | |
| - | * **extends: | + | |
| - | + | ||
| - | Τόσο στις κλάσεις όσο και στα interfaces ορίζεται η ιδιότητα της κληρονομικότητας. __Η διαφορά είναι ότι ενώ στην κλάση μπορούμε να έχουμε μόνο μία γονική κλάση, στα interfaces μπορούμε να έχουμε περισσότερα του ενός γονικά interfaces__. | + | |
| - | + | ||
| - | <WRAP tip 80% center round> | + | |
| - | Ακριβώς όπως και οι κλάσεις κάθε interface πρέπει να βρίσκεται σε ξεχωριστό αρχείο, | + | |
| - | </ | + | |
| - | + | ||
| - | ==== Το σώμα του Interface ==== | + | |
| - | + | ||
| - | Ένα // | + | |
| - | + | ||
| - | <code java> | + | |
| - | double E = 2.718282; | + | |
| - | double PI = 3.14159; | + | |
| - | </ | + | |
| - | + | ||
| - | Τα πεδία αυτά εξ ορισμού (by default) '' | + | |
| - | + | ||
| - | Οι μέθοδοι σε ένα interface κατά κανόνα είναι **abstract**, | + | |
| - | + | ||
| - | <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); | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Παράδειγμα interface - H Στοίβα (Stack) ===== | + | |
| - | + | ||
| - | Ας υποθέσουμε ότι θέλουμε να δηλώσουμε μέσω ενός interface της μεθόδους που πρέπει να υποστηρίζει μία στοίβα. Η στοίβα είναι μία δομή αποθήκευσης δεδομένων τύπου **Last In First Out (LIFO)**, όπου το αντικείμενο αποθηκεύεται στη στοίβα τελευταίο εξάγεται από αυτή πρώτο. Κάθε στοίβα θα πρέπει να υποστηρίζει κατ' | + | |
| - | * μέθοδο επιστροφής του μεγέθους της στοίβας. | + | |
| - | * μέθοδο ένθεσης στην κορυφή της στοίβας. | + | |
| - | * μέθοδο απόσβεσης του κορυφαίου στοιχείου. | + | |
| - | * μέθοδο επιστροφής του κορυφαίου στοιχείου της στοίβας χωρίς διαγραφή από τη στοίβα. | + | |
| - | + | ||
| - | Παρακάτω δηλώνεται το //interface Stack// που υποστηρίζει αυτές τις λειτουργίες | + | |
| - | <code java Stack.java> | + | |
| - | public interface Stack { | + | |
| - | public int size(); | + | |
| - | public void push(Object o); // ένθεση στην κορυφή της στοίβας. | + | |
| - | public Object pop(); | + | |
| - | public Object top(); | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Υλοποιώντας το Interface ===== | + | |
| - | + | ||
| - | Μία κλάση υλοποιεί ένα interface μόνο εάν υλοποιεί ΟΛΕΣ τις μεθόδους του interface. Έάν έστω και μία μέθοδος δεν υλοποιείται τότε η κλάση δεν υλοποιεί το interface. | + | |
| - | + | ||
| - | Σε συνέχεια του προηγούμενου παραδείγματος θα επιχειρήσουμε να δημιουργήσουμε δύο διαφορετικές κλάσεις που υλοποιούν το συγκεκριμένο interface. Οι κλάσεις αυτές είναι η ArrayStack που υλοποιεί τη στοίβα μέσω ενός πίνακα και η LinkedStack που υλοποιεί τη στοίβα διασυνδέοντας επιμέρους αντικείμενα μεταξύ τους σε αναλογία με μία διασυνδεδεμένη λίστα. | + | |
| - | + | ||
| - | ==== ArrayStack ==== | + | |
| - | + | ||
| - | <code java ArrayStack.java> | + | |
| - | public class ArrayStack implements Stack { | + | |
| - | public int capacity; | + | |
| - | public Object | + | |
| - | public int size; | + | |
| - | + | ||
| - | public ArrayStack() { | + | |
| - | this(256); | + | |
| - | } | + | |
| - | + | ||
| - | public ArrayStack(int capacity) { | + | |
| - | array = new Object[capacity]; | + | |
| - | this.capacity = capacity; | + | |
| - | size = 0; | + | |
| - | } | + | |
| - | + | ||
| - | public int size() { | + | |
| - | return size; | + | |
| - | } | + | |
| - | + | ||
| - | public void push(Object o) { | + | |
| - | + | ||
| - | if( size >= capacity-1 ) { | + | |
| - | capacity *= 2; | + | |
| - | Object []newArray = new Object[capacity]; | + | |
| - | for(int i=0; i< | + | |
| - | newArray[i] = array[i]; | + | |
| - | array = newArray; | + | |
| - | newArray = null; | + | |
| - | } | + | |
| - | array[size++] = o; | + | |
| - | } | + | |
| - | + | ||
| - | public Object pop() { | + | |
| - | return array[--size]; | + | |
| - | } | + | |
| - | + | ||
| - | public Object top() { | + | |
| - | return array[size-1]; | + | |
| - | } | + | |
| - | + | ||
| - | public String toString() { | + | |
| - | String str = " | + | |
| - | for(int i=size-1; i>=0; i--) | + | |
| - | str += array[i].toString()+" | + | |
| - | return str + " | + | |
| - | } | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | ==== LinkedStack ==== | + | |
| - | + | ||
| - | + | ||
| - | < | + | |
| - | + | ||
| - | class LinkedNode { | + | |
| - | private Object o; | + | |
| - | private LinkedNode next; | + | |
| - | + | ||
| - | public LinkedNode(LinkedNode nxt, Object e) { | + | |
| - | next = nxt; | + | |
| - | o = e; | + | |
| - | } | + | |
| - | + | ||
| - | public LinkedNode(Object e) { | + | |
| - | this(null, e); | + | |
| - | } | + | |
| - | + | ||
| - | public Object getElement() { return o; } | + | |
| - | public LinkedNode getNext() { return next; } | + | |
| - | + | ||
| - | public void setElement(Object e) { o = e; } | + | |
| - | public void setNext(LinkedNode node) { next = node; } | + | |
| - | } | + | |
| - | + | ||
| - | public class LinkedStack implements Stack { | + | |
| - | int size; | + | |
| - | LinkedNode head; | + | |
| - | + | ||
| - | public LinkedStack() { | + | |
| - | size=0; | + | |
| - | head = null; | + | |
| - | } | + | |
| - | + | ||
| - | public int size() { | + | |
| - | return size; | + | |
| - | } | + | |
| - | + | ||
| - | public void push(Object o) { | + | |
| - | // the following is OK even if head == null. | + | |
| - | head = new LinkedNode(head, | + | |
| - | size++; | + | |
| - | } | + | |
| - | + | ||
| - | public Object pop() { | + | |
| - | LinkedNode pN = head; | + | |
| - | head = head.getNext(); | + | |
| - | size--; | + | |
| - | return pN.getElement(); | + | |
| - | } | + | |
| - | + | ||
| - | public Object top() { | + | |
| - | return head; | + | |
| - | } | + | |
| - | + | ||
| - | public String toString() { | + | |
| - | String str = " | + | |
| - | LinkedNode curr = head; | + | |
| - | while(curr != null) | + | |
| - | str += curr.getElement().toString(); | + | |
| - | return str + " | + | |
| - | } | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | Παρατηρούμε δηλαδή ότι έχουμε υλοποιήσει το συγκεκριμένο interface μέσα από δύο διαφορετικές κλάσεις. Αν και η υλοποίηση των κλάσεων διαφέρει σημαντικά, | + | |
| - | + | ||
| - | + | ||
| - | ===== Χρησιμοποιώντας ένα interface ως τύπο δεδομένων ===== | + | |
| - | + | ||
| - | Ας υποθέσουμε τώρα ότι θέλουμε να χρησιμοποιήσουμε μία στοίβα για να αντιμεταθέσουμε τα στοιχεία ενός πίνακα χαρακτήρων. Ο πίνακας αρχικά περιέχει την αγγλική αλφαβήτα (26 χαρακτήρες) και θέλουμε να αντιστρέψουμε την σειρά με την οποία αποθηκεύονται οι χαρακτήρες στον πίνακα. Η μέθοδος invertArray παρακάτω αντιμεταθέτει τα στοιχεία του πίνακα array με χρήση της στοίβας stk. | + | |
| - | + | ||
| - | <code java ArrayManipulator.java> | + | |
| - | public class ArrayManipulator { | + | |
| - | public static void main(String []args) { | + | |
| - | Character alphabet[] = {' | + | |
| - | printCharacterArray(alphabet); | + | |
| - | System.out.println(" | + | |
| - | + | ||
| - | Stack stack = new ArrayStack(); | + | |
| - | invertArray(alphabet, | + | |
| - | + | ||
| - | printCharacterArray(alphabet); | + | |
| - | } | + | |
| - | + | ||
| - | public static void invertArray(Object []array, Stack stk) { | + | |
| - | for(int i=0; i< | + | |
| - | stk.push(array[i]); | + | |
| - | for(int i=0; i< | + | |
| - | array[i] = stk.pop(); | + | |
| - | } | + | |
| - | + | ||
| - | public static void printCharacterArray(Character []array) { | + | |
| - | for(Character c : array) | + | |
| - | System.out.print(c+" | + | |
| - | System.out.println("" | + | |
| - | } | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP important 80% center round> | + | |
| - | Παρατηρήστε ότι η μέθοδος // | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP todo 80% center round> | + | |
| - | Στο παραπάνω πρόγραμμα, | + | |
| - | <code java> | + | |
| - | Stack stack = new ArrayStack(); | + | |
| - | </ | + | |
| - | σε | + | |
| - | <code java> | + | |
| - | Stack stack = new LinkedStack(); | + | |
| - | </ | + | |
| - | + | ||
| - | Παρατηρήστε αν σας βγάζει κάποιο μήνυμα ο // | + | |
| - | </ | + | |
| - | + | ||
| - | ===== Μεταβάλλοντας ένα υφιστάμενο 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 | Περιεχόμενα ]] | Επόμενο: | + | |