====== Παράδειγμα χρήσης διεπαφής σε συνδυασμό με Abstract κλάση (περιέχει και generics στο τελευταίο μέρος της λύσης) ====== Ας επανέλθουμε στο παράδειγμα [[java:interface_as_data_type|χρήσης των interfaces ως τύπους δεδομένων]] και ας αλλάξουμε ελαφρά το interface MyComparable ώστε να περιέχει και την μέθοδο isEqual που ελέγχει την ισότητα. public interface MyComparable { public boolean isLarger(MyComparable other) throws InvalidComparableTypeException; public boolean isEqual(MyComparable other) throws InvalidComparableTypeException; } Η κλάση **InvalidComparableTypeException** ορίζεται ως εξής: class InvalidComparableTypeException extends Exception { } και είναι απαραίτητη ώστε να σηματοδοτήσουμε την πιθανή σύγκριση ασύμβατων μεταξύ τους τύπων δεδομένων. Στη συνέχεια κατασκευάζουμε την κλάση Rectangle που υλοποιεί το συγκεκριμένο interface: public class Rectangle implements 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 boolean isLarger(MyComparable other) throws InvalidComparableTypeException { if( other instanceof Rectangle ) { Rectangle otherRect = (Rectangle) other; return (this.getArea() > otherRect.getArea()); } throw new InvalidComparableTypeException(); } @Override public boolean isEqual(MyComparable other) throws InvalidComparableTypeException { if( other instanceof Rectangle ) { Rectangle otherRect = (Rectangle) other; return (this.getArea() == otherRect.getArea()); } throw new InvalidComparableTypeException(); } @Override public String toString() { return origin.toString() + ", " + width + " X " + height ; } public double getArea() { return (double)width * height; } } Παρατηρήστε ότι υλοποιούμε το //interface MyComparable// συγκρίνοντας τα αντικείμενου τύπου Rectangle __με βάση το εμβαδόν τους__. Επιπλέον ορίζουμε την κλάση //MyComparableUtil// που δίνει δύο μεθόδους σύγκρισης αντικείμενων τύπου MyComparable: public class MyComparableUtil { /** * Returns the larger among the two objects. If objects are equal returns null. */ public static MyComparable findLarger(MyComparable object1, MyComparable object2) throws InvalidComparableTypeException { if(object1.isLarger(object2)) return object1; else if( object1.isEqual(object2) ) return null; return object2; } /** * Returns the smaller among the two objects. If objects are equal returns null. */ public static MyComparable findSmaller(MyComparable object1, MyComparable object2) throws InvalidComparableTypeException { if(!object1.isLarger(object2)) return object1; else if( object1.isEqual(object2) ) return null; return object2; } } Τέλος η κλάση //CompareObjects// παρέχει τη //main// μέθοδο που θα χρειαστούμε για να εκτελέσουμε το πρόγραμμα. public class CompareObjects { public static void main (String []args) { try { Point p = new Point(1,1); Rectangle rec1 = new Rectangle(10, 20, p); Rectangle rec2 = new Rectangle(10, 22, p); System.out.println("Larger objet is "+ MyComparableUtil.findLarger(rec1, rec2)); } catch(InvalidComparableTypeException ex) { System.err.println("Unable to compare objects!"); } } } Ας υποθέσουμε τώρα ότι θέλετε να φτιάξετε την κλάση //Circle// που απεικονίζει τον κύκλο και υλοποιεί και αυτή το //interface MyComparable//. public class Circle implements MyComparable{ private Point origin; private int radius; public Circle(Point origin, int radius) { this.origin = origin; this.radius = radius; } public void setOrigin(Point newOrigin) { origin = newOrigin; } public Point getOrigin() { return origin; } public void setRadius(int newRadius ) { radius = newRadius; } public int getRadius() { return radius; } public double getArea() { return 3.14159 * radius * radius; } @Override public boolean isLarger(MyComparable other) throws InvalidComparableTypeException { if( other instanceof Circle ) { Circle otherRect = (Circle) other; return (this.getArea() > otherRect.getArea()); } throw new InvalidComparableTypeException(); } @Override public boolean isEqual(MyComparable other) throws InvalidComparableTypeException { if( other instanceof Circle ) { Circle otherRect = (Circle) other; return (this.getArea() == otherRect.getArea()); } throw new InvalidComparableTypeException(); } public String toString() { return origin.toString()+" radius: "+radius; } } Ας προσπαθήσουμε τώρα στην //CompareObjects// να συγκρίνουμε ένα ορθογώνιο παραλληλόγραμμο με ένα κύκλο. public class CompareObjects { public static void main (String []args) { try { Point p = new Point(1,1); Rectangle rect = new Rectangle(10, 20, p); Circle circle = new Circle(p, 12); System.out.println("Larger objet is "+ MyComparableUtil.findLarger(rect, circle)); } catch(InvalidComparableTypeException ex) { System.err.println("Unable to compare objects!"); } } } Παρατηρούμε ότι η παραπάνω κλάση μεταγλωττίζεται, αλλά όταν θα επιχειρήσετε να την τρέξετε θα παραχθεί το exception //InvalidComparableTypeException// και θα τυπωθεί το μήνυμα μέσα στο //catch block//. Eίναι αδύνατον να συγκρίνουμε διδιάστατα αντικείμενα που ανήκουν σε διαφορετικά σχήματα, αν και το μέτρο σύγκρισης του εμβαδού που υιοθετούμε είναι κοινό και στις δύο κλάσεις. Προκειμένου να μπορούμε να συγκρίνουμε διαφορετικά διδιάστατα σχήματα μεταξύ τους η λύση είναι να φτιάξουμε μία ενδιάμεση κλάση η οποία να συγκρίνει τα διδιάστατα σχήματα με βάση το εμβαδό τους, δηλαδή με βάση την μέθοδο **getArea()**. Ας ονομάσουμε την κλάση αυτή **TwoDShape**: public abstract class TwoDShape implements MyComparable { public final boolean isLarger(MyComparable other) throws InvalidComparableTypeException { if( other instanceof TwoDShape ) { TwoDShape obj = (TwoDShape)other; if( this.getArea() > obj.getArea() ) return true; else return false; } throw new InvalidComparableTypeException(); } public final boolean isEqual(MyComparable other) throws InvalidComparableTypeException { if( other instanceof TwoDShape ) { TwoDShape obj = (TwoDShape)other; if( this.getArea() == obj.getArea() ) return true; else return false; } throw new InvalidComparableTypeException(); } public abstract double getArea(); } Παρατηρήστε τα εξής: - Η μέθοδος isLarger για τα διδιάστατα σχήματα βασίζεται στη μέθοδο getArea(). Η μέθοδος getArea() ορίζεται στην κλάση TwoDShape, αλλά η υλοποίηση της εξαρτάται από το εκάστοτε διδιάστατο σχήμα. Κατά συνέπεια, η μέθοδος δηλώνεται ως abstract. Η μέθοδος getArea() θα οριστεί σε κάθε υποκλάση της TwoDShape. Η κλάση που περιέχει μία abstract μέθοδο είναι και αυτή abstract. - H μέθοδος isLarger συγκρίνει τα διδιάστατα σχήματα με βάση το εμβαδό τους. Προκειμένου κάποια υποκλάση να μην επαναορίσει την μέθοδο isLarger με τρόπο που δεν είναι επιθυμητός, ώστε να εξακολουθούμε να μπορούμε να συγκρίνουμε διδιάστατα σχήματα μεταξύ τους, ορίζουμε την μέθοδο ως final. Με βάση τα παραπάνω οι κλάσεις Rectangle και Circle γίνονται ως εξής: public class Rectangle extends TwoDShape { // 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 String toString() { return origin.toString() + ", " + width + " X " + height ; } public double getArea() { return width * height; } } public class Circle extends TwoDShape { private Point origin; private int radius; public Circle(Point origin, int radius) { this.origin = origin; this.radius = radius; } public void setOrigin(Point newOrigin) { origin = newOrigin; } public Point getOrigin() { return origin; } public void setRadius(int newRadius ) { radius = newRadius; } public int getRadius() { return radius; } public double getArea() { return 3.14159 * radius * radius; } public String toString() { return origin.toString()+" radius: "+radius; } } Επιχειρώντας τώρα να τρέξετε τη κλάση CompareObjects παρατηρήστε ότι τρέχει χωρίς πρόβλημα. ===== Εισάγοντας μία νέα κλάση που υλοποιεί το Interface ===== Σε αναλογία με τα παραπάνω ας υποθέσουμε ότι φτιάχνουμε την κλάση ThreeDShape που υλοποιεί και αυτή το interface MyComparable. Η κλάση ThreeDShape έχει και αυτή την δική της υποκλάση Cuboid. public abstract class ThreeDShape implements MyComparable { public final boolean isLarger(MyComparable other) throws InvalidComparableTypeException { if( other instanceof ThreeDShape ) { ThreeDShape obj = (ThreeDShape)other; if( this.getArea() > obj.getArea() ) return true; else return false; } throw new InvalidComparableTypeException(); } public final boolean isEqual(MyComparable other) throws InvalidComparableTypeException { if( other instanceof ThreeDShape ) { ThreeDShape obj = (ThreeDShape)other; if( this.getArea() == obj.getArea() ) return true; else return false; } throw new InvalidComparableTypeException(); } public abstract double getVolume(); public abstract double getArea(); } public class Cuboid extends ThreeDShape { Rectangle rec; int depth; public Cuboid( Rectangle rec, int depth) { this.rec = rec; this.depth = depth; } public double getVolume() { return rec.getArea() * depth; } public double getArea() { return 2 * ( depth * rec.getWidth() + depth * rec.getHeight() + rec.getArea() ); } public String toString() { return rec.toString() + " ,depth: "+depth; } } Ας υποθέσουμε τώρα ότι επιχειρούμε συγκρίνουμε ένα διδιάστατο με ένα τρισδιάστατο σχήμα. Μεταβάλλουμε κατάληλλα την κλάση //CompareObjects// και επιχειρούμε να μεταγλωττίσουμε και να τρέξουμε. public class CompareObjects { public static void main (String []args) { try { Point p = new Point(1,1); Rectangle rect = new Rectangle(10, 20, p); Cuboid cuboid = new Cuboid(rect, 15); System.out.println("Larger objet is "+ MyComparableUtil.findLarger(rect, cuboid)); } catch(InvalidComparableTypeException ex) { System.err.println("Unable to compare objects!"); } } } Ο παρακάτω κώδικας δεν θα έπρεπε να δουλεύει καθώς επιχειρεί να συγκρίνει ένα διδιάστατο με ένα τρισδιάστατο σχήμα που είναι μη συγκρίσιμα. Πράγματι δεν δουλεύει καθώς παράγει ένα exception InvalidComparableTypeException. __Ιδανικά όμως θα θέλαμε ένα σφάλμα αυτού του τύπου να το αντιληφθούμε κατά την μεταγλώττιση του προγράμματος και όχι κατά την εκτέλεση του__. Η λύση στο πρόβλημα είναι η χρήση παραμετρικών τύπων δεδομένων (generics). __Οι παραμετρικοί τύποι δεδομένων έχουν την δυνατότητα να αντιλαμβάνονται ασυμβατότητες τύπων δεδομένων κατά την μεταγλώττιση και όχι κατά την εκτέλεση__. Δείτε πως διαμορφώνονται όλες οι παραπάνω κλάσεις με χρήση generics. ===== Χρήση Generics για το προηγούμενο παράδειγμα ===== public interface MyComparable { public boolean isLarger(T other); public boolean isEqual(T other); } public abstract class TwoDShape implements MyComparable { public final boolean isLarger(TwoDShape other) { if( this.getArea() > other.getArea() ) return true; else return false; } public final boolean isEqual(TwoDShape other) { if( this.getArea() == other.getArea() ) return true; else return false; } public abstract double getArea(); } public abstract class ThreeDShape implements MyComparable { public final boolean isLarger(ThreeDShape other) { if( this.getArea() > other.getArea() ) return true; else return false; } public final boolean isEqual(ThreeDShape other) { if( this.getArea() == other.getArea() ) return true; else return false; } public abstract double getArea(); public abstract double getVolume(); } public class MyComparableUtil > { public T findLarger(T object1, T object2) { if (object1.isLarger(object2)) return object1; else if (object1.isEqual(object2)) return null; else return object2; } public T findSmaller(T object1, T object2) { if (!(object1.isLarger(object2))) return object1; else if (object1.isEqual(object2)) return null; else return object2; } } Παρατηρήστε τα εξής: - Δεν έχει νόημα η παραγωγή του InvalidComparableTypeException, καθώς η ασυμβατότητα των τύπων εντοπίζεται κατά την διάρκεια της μεταγλώττισης και όχι της εκτέλεσης του κώδικα. - Δεν είναι δυνατόν να ορίσετε την κλάση //MyComparableUtil// ως στατική κλάση, καθώς περιέχει παραμετρικούς τύπους και ο compiler δεν το επιτρέπει. Προσαρμόζουμε κατάλληλα την κλάση //CompareObjects//, ώστε να ανταποκρίνεται στις παραπάνω αλλαγές. Αν προσπαθήσετε να μεταγλωττίσετε την CompareObjects θα δείτε ότι είναι αδύνατον να το κάνετε. public class CompareObjects { public static void main (String []args) { Point p = new Point(1,1); Rectangle rect = new Rectangle(10, 20, p); Cuboid cuboid = new Cuboid(rect, 15); MyComparableUtil util = new MyComparableUtil<>(); System.out.println("Larger objet is "+ util.findLarger(rect, cuboid)); } } Το μήνυμα που εμφανίζει ο compiler είναι το παρακάτω CompareObjects.java:9: error: method findLarger in class MyComparableUtil cannot be applied to given types; System.out.println("Larger objet is "+ util.findLarger(rect, cuboid)); ^ required: TwoDShape,TwoDShape found: Rectangle,Cuboid reason: actual argument Cuboid cannot be converted to TwoDShape by method invocation conversion where T is a type-variable: T extends MyComparable declared in class MyComparableUtil | Προηγούμενο : [[ :java:abstract_classes_vs_interfaces | Συγκρίνοντας Abstract Κλάσεις και Interfaces ]] | [[ :toc | Περιεχόμενα ]] | Επόμενο: [[ :java:comparable | Συγκρίνοντας αντικείμενα μεταξύ τους ]] |