Αντικειμενοστραφής Προγραμματισμός I (5 ο εξ) Διάλεξη #6 η : Η λέξη κλειδί this, υπερφόρτωση μεθόδων, κληρονομικότητα, πολυμορφισμός, υπερκάλυψη, επίπεδα προσπέλασης Γαβαλάς Δαμιανός dgavalas@aegean.gr Η λέξη κλειδί this Σε μια μέθοδο αν θέλουμε να αναφερθούμε στο αντικείμενο που την κάλεσε (τρέχον αντικείμενο) αντί του ονόματος του αντικειμένου μπορούμε μ να γράψουμε τη λέξη κλειδί this Το this το χρησιμοποιούμε μέσα στις μεθόδους για να αναφερθούμε σε ένα οποιοδήποτε αντικείμενο μπορεί να παραχθεί από την κλάση στην οποία βρισκόμαστε Σημαίνει απλά: this object here! Το this χρησιμοποιείτα συνήθως είτε για να περάσουμε το τρέχον αντικείμενο ως παράμετρο σε μεθόδους άλλων κλάσεων είτε για να καλέσουμε, μέσα από ένα κατασκευαστή της τρέχουσας κλάσης έναν άλλο κατασκευαστή. 1
Μέθοδοι και this class Car { String licenseplate = ""; // e.g. "New York 543 A23" double speed = 0.0; // in kilometers per hour double maxspeed; = 120.0; // in kilometers per hour // επιτάχυνε στο maximum void gomaximum() { this.speed = this.maxspeed; void accelerate (double deltav) { this.speed = this.speed + deltav; if (this.speed speed > this.maxspeed) { this.speed = this.maxspeed; if (this.speed < 0.0) { this.speed = 0.0; Μέθοδοι και this class CarTest { public static void main( String[] args) { Car c = new Car(); c.licenseplate = "New York A45 636"; c.maxspeed = 123.45; System.out.println(c.licensePlate + " is moving at " + c.speed + " kilometers per hour."); for (int i = 0; i < 15; i++) { c.accelerate(10.0); System.out.println( c.licenseplate + " is moving at " + c.speed + " kilometers per hour."); 2
Υπερφόρτωση Μεθόδων Δύο μέθοδοι με το ίδιο όνομα αλλά διαφορετικά ορίσματα είναι διαφορετικές μέθοδοι Δεν πρέπει αναγκαστικά να επιστρέφουν τον ίδιο τύπο, πρέπει να έχουν διαφορετικά ορίσματα Η τεχνική αυτή, λέγεται υπερφόρτωση μεθόδων (method overloading) Συνήθως επιλέγουμε οι μέθοδοι αυτές να κάνουν την ίδια δουλειά αλλά με διαφορετικό τρόπο Υπερφόρτωση Μεθόδων: Παράδειγμα Η μέθοδος indexof της κλάσης String: public int indexof (String str) public int indexof (int ch) public int indexof (String str, int fromindex) public int indexof (int ch, int fromindex) 3
Κλήση υπερφορτωμένου κατασκευαστή public class Circle { private double radius; public Circle(double radius) { this.radius = radius; public Circle() { this(1.0); To this αναφέρεται στην ιδιότητα radiusτου αν τικειμένου που δημιουργείται To this χρησιμοποιείται για να καλέσει το ν άλ λο κατασκευαστή public double getarea() { return this.radius * this.radius * Math.PI; Κάθε ιδιότητα ανήκει σε ένα στιγμιότυπο του αν τικειμένου που αναπαρίσταται από το this, το οποίο συνήθως παραλείπεται. Υπερφόρτωση Μεθόδων: Παράδειγμα public class Rectangle { double x1, y1, x2, y2; Rectangular builtrect (double a, double b, double c, double d) { x1 = a; y1 = a; x2 = a; y2 = a; return this; Rectangular builtrect (Point a, Point b){ x1 = a.getx(); y1 = a.gety(); x2 = b.getx(); y2 = b.gety(); return this; public static void main(string[] args) { Rectangle r = new Rectangular(); Rectangle r1 = rbuiltrect(new r.builtrect(new Point(3,3), new Point(5,5)); Rectangle r2 = r.builtrect(3,3,6,6); 4
Υπερφόρτωση κατασκευαστών: Παράδειγμα public class Rectangular { double x1, y1, x2, y2; public Rectangular (double a, double b, double c, double d) { x1 = a; y1 = a; x2 = a; y2 = a; public Rectangular () { this (1,1,2,2); Κληρονομικότητα (inheritance) Μια υποκλάση (subclass) κληρονομεί όλες τις ιδιότητες και μεθόδους των υπερκλάσεων (superclass) αυτής Μια κλάση μπορεί να έχει και υποκλάσεις (subclasses) οι οποίες δείχνουν πως το γενικό πρότυπο εξειδικεύεται σε πιο «στενά» πρότυπα Π.χ. η υπερκλάση Άνθρωπος μπορεί να έχει ως υποκλάσεις τις κλάσεις άνδρας, γυναίκα, φοιτητής, υπάλληλος λ Η υποκλάση δημιουργείται επεκτείνοντας μια κλάση (χρησιμοποιείται η λέξη κλειδί extends) Διατηρεί όλα τα χαρακτηριστικά (μέθοδοι, ιδιότητες) της κλάσης και προσθέτει κάποια επιπλέον δικά της (που αφορούν μόνο αυτή) 5
Υπερκλάσεις και υποκλάσεις GeometricObject -color: String -filled: boolean -datecreated: java.util.date +GeometricObject() +getcolor(): String +setcolor(color: String): void +isfilled (): boolean +setfilled(filled: boolean): void +getdatecreated(): java.util.date +tostrin g(): String The color of the object (defau lt: white). Indicates whether the object is filled with a color (default: false). The date when the object was created. Creates a GeometricObject. Returns the color. Sets a new color. Returns the filled prop erty. Sets a new filled property. Returns the datecreated. Returns a string representation of this object. Circle -radius: double +Circle() +Circle(radius: double) +getradius(): double +setrad ius(radius: double): void +getarea(): d ou ble +getperimeter(): double +getdiameter(): double Rectangle -width: wd double -height: double +Rectangle() +Rectangle(wid th: double, height: double) +getwidth(): double +setwidth(width: double): void +getheight(): double +setheight(height: double): void +getarea(): double +getperimeter(): double Οι κατασκευαστές των υπερκλάσεων κληρονομούνται; Όχι, δεν κληρονομούνται. Καλούνται άμεσα ή έμμεσα. Η άμεση κλήση γίνεται με χρήση της λέξης κλειδί super. Σε περίπτωση που η λέξη κλειδί super δε χρησιμοποιηθεί άμεσα, τότε καλείται ο κατασκευαστής της υπερκλάσης που δε δέχεται ορίσματα. 6
Προσοχή! Πρέπει να χρησιμοποιείτε τη λέξη κλειδί super για να καλέσετε τον κατασκευαστή της υπερκλάσης Το να καλέσετε τον κατασκευαστή της υπερκλάσης με το όνομά του, αντί με το super(), στην υποκλάση δημιουργεί συντακτικό λάθος Η Java απαιτεί η δήλωση που απαιτεί τη λέξη κλειδί super να εμφανίζεται πρώτη στον κατασκευαστή της υποκλάσης Μέθοδοι και super import java.awt.point; class NamedPoint extends Point { String name; NamedPoint(int x, int y, String name) { super(x,y); this.name = name; public static void main (String[] arguments) { NamedPoint np = new NamedPoint(5, 5, "SmallPoint"); System.out.println("x is " + np.x); System.out.println("y is " + np.y); System.out.println("Name is " + np.name); 7
Ορίζοντας μια υποκλάση Μια υποκλάση κληρονομεί τις ιδιότητες και μεθόδους της υπερκλάσης της Μπορεί ωστόσο και να την επεκτείνει : Προσθέτοντας νέες ιδιότητες Προσθέτοντας νέες μεθόδους Υπερκαλύπτοντας τις μεθόδους της υπερκλάσεις Υπερκαλύπτοντας μεθόδους των υπερκλάσεων Μια υποκλάση κληρονομεί τις μεθόδους της υπερκλάσηςτης Κάποιες φορές είναι απαραίτητο μια υποκλάση να τροποποιήσει την υλοποίηση μιας μεθόδου που ορίζεται στην υπερκλάση Αυτή η τροποίηση ονομάζεται υπερκάλυψη (overriding) public class Circle extends GeometricObject { // άλλες μέθοδοι παραλείπονται /** Override the tostring method defined in GeometricObject */ public String tostring() { return super.tostring() + "\nradius is " + radius; 8
Σημείωση Μία μέθοδος μπορεί να υπερκαλυφθεί μόνο εφόσον είναι προσβάσιμη Έτσι, μια private μέθοδος δεν μπορεί να υπερκαλυφθεί καθώς δεν είναι προσβάσιμη εκτός της δικής της κλάσης Αν μια μέθοδος που ορίζεται σε μια υποκλάση είναι private στην υπερκλάση της, οι δύο μέθοδοι είναι εντελών ασυσχέτιστες Οι static μέθοδοι κληρονομούνται αλλά δεν υπερκαλύπτονται Υπερκάλυψη έναντι υπερφόρτωσης (overriding vs overloading public class Test { public static void main(string[] args) { A a = new A(); a.p(10); class B { public void p(int i) { class A extends B { // This method overrides the method in B public void p(int i) { System.out.println(i); public class Test { public static void main(string[] args) { A a = new A(); a.p(10); class B { public void p(int i) { class A extends B { // This method overloads the method in B public void p(double i) { System.out.println(i); 9
H κλάση Object Κάθε κλάση στην Java «κατάγεται» από (κληρονομεί) την κλάση java.lang.object Εφόσον δεν ορίζεται κληρονομικότητα η στον ορισμό μιας κλάσης, η υπερκλάση της κλάσης είναι η Object. public class Circle { public class Circle extends Object { αντίστοιχο...... Η μέθοδος tostring() στην κλάση Object Η μέθοδος tostring() της κλάσης java.lang.object επιστρέφει μια συμβολοσειρά που αναπαριστά το αντικείμενο (τυπικά η συμβολοσειρά αυτή περιλαμβάνει το όνομα της κλάσης της οποίας το αντικείμενο αποτελεί στιγμιότυπο, το «παπάκι» (@), και ένα αριθμό που αναπαριστά το αντικείμενο. Loan loan = new Loan(); System.out.println(loan.toString()); tostring()); Ο παραπάνω κώδικας τυπώνει κάτι σαν Loan@15037e5. Για να τυπωθεί κάτι πιο χρήσιμο, πρέπει να υλοποιήσεις μια μέθοδο που υπερκαλύπτει την tostring ώστε να εμφανίζει μια πιο εύγλωτη αναπαράσταση του αντικειμένου 10
Πολυμορφισμός (Polymorphism) public class PolymorphismDemo { public static void main(string[] args) { m(new GraduateStudent()); m(new Student()); m(new Person()); m(new Object()); public static void m(object x) { System.out.println(x.toString()); class GraduateStudent extends Student { class Student extends Person { public String tostring() { return "Student"; class Person extends Object { public String tostring() { return "Person"; Η μέθοδος m δέχεται ως παράμετρο ένα αντικείμενο τύπου Object. Την καλείς περνώντας τις οποιοδήποτε αντικείμενο. Ένα αντικείμενο κάποιας υποκλάσης μπορεί να χρησιμοποιηθεί όποτε απαιτείται αντικείμενο της υπερκλάσης. Αυτό το χαρακτηριστικό ονομάζεται πολυμορφισμός (polymorphism). Όταν εκτελείται η μέθοδοος m(object x), καλείται η tostring μέθοδος της παραμέτρου x. Το x μπορεί να είναι στιγμιότυπο των κλάσεων GraduateStudent, Student, Person, ή Object. Οι κλάσεις GraduateStudent, Student, Person και Object έχουν τη δική τους υλοποίηση της μεθόδου tostring. Το ποια υλοποίηση θα χρησιμοποιηθεί αποφασίζεται δυναμικά από τη Java Virtual Machine κατά το χρόνο εκτέλεσης. Αυτή η δυνατότητα ονομάζεται δυναμική σύνδεση (dynamic binding). Μετατροπή αντικειμένων (Casting Objects) Έχουμε ήδη δει μετατροπές μεταβλητών από έναν πρωτογενή τύπο σε άλλον (type casting): float f = 8.9; int i = (int)f ). Μετατροπές μπορούν να γίνουν και από ένα αντικείμενο μιας κλάσης σε αντικείμενο κάποιας άλλης κλάσης. Στο προηγούμενο παράδειγμα, η παρακάτω δήλωση εκχωρεί ένα αντικείμενο της κλάσης Student σε μια παράμετρο τύπου Object: m(new Student()); Αυτή η δήλωση είναι αντίστοιχη με το παρακάτω (καθώς ένα στιγμιότυπο της Student είναι αυτομάτως, λόγω κληρονομικότητας, και στιγμιότυπο της Object: Object o = new Student(); // έμμεση μετατροπή m(o); 11
Γιατί το Casting είναι απαραίτητο; Αν όμως προσπαθήσουμε να εκχωρήσουμε ένα αντικείμενο της Object σε μια μεταβλητή τύπου Student: Student b = o; θα πάρουμε λάθος ςμεταγλώττισης ης( (καθώς ς ο Student, ως ειδικότερος τύπος, είναι Object ενώ το Object δεν είναι Student Δηλαδή, παρότι το ο είναι όντως μεταβλητή τύπου Student, o compiler δε το δέχεται. Για να επιτύχουμε τη μετατροπή, γράφουμε: Student b = (Student)o; // Άμεση μετατροπή Άρα, το casting χρησιμοποιείται μόνο για τη μετατροπή από την υπερκλάση στην υποκλάση Ο τελεστής instanceof Ο τελεστής instanceof ελέγχει κατά πόσο ένα αντικείμενο αποτελεί πράγματι στιγμιότυπο μιας κλάσης: Object myobject = new Circle();... // Some lines of code /** κάνε τη μετατροπή εφόσον το myobject είναι στιγμιότυπο της κλάσης Circle */ if (myobject instanceof Circle) { System.out.println("The circle diameter is " + ((Circle)myObject).getDiameter());... 12
Επίπεδα Προσπέλασης Υπάρχουν λέξεις κλειδιά που ελέγχουν ποιος βλέπει ποια κλάση, μέθοδο, μεταβλητή Αυτές είναι με σειρά προσπελασιμότητας οι: private (ιδιωτική προσπέλαση) protected (προστατευμένη προσπέλαση) χωρίς λέξη κλειδί είναι η προεπιλεγμένη (default) προσπέλαση public (δημόσια ό προσπέλαση) ) Παράδειγμα public int MyMethod(int x, int y) Σύνοψη προσπελασιμότητας Θέα Από την ίδια Από κάθεάλλη άλλη Από μία Από οποιαδήποτε κλάση κλάση στο ίδιο υποκλάση κλάση σε άλλο πακέτο πακέτο public protected - default - - private - - - 13
package p1; Επίπεδα Προσπέλασης public class C1 { public int x; protected int y; int z; private int u; protected void m() { public class C2 { C1 o = new C1(); can access o.x; can access o.y; can access o.z; cannot access o.u; can invoke o.m(); package p2; public class C3 extends C1 { can access x; can access y; can access z; cannot access u; can invoke m(); public class C4 extends C1 { can access x; can access y; cannot access z; cannot access u; can invoke m(); public class C5 { C1 o = new C1(); can access o.x; cannot access o.y; cannot access o.z; cannot access o.u; cannot invoke o.m(); Το επίπεδο προσπέλασης final Ηfinal κλάση δεν κληρονομείται: final class Math {... Η final μεταβλητή είναι σταθερή: final static double PI = 3.14159; Η final μέθοδος δεν μπορεί να υπερκαλυφθεί στις υποκλάσεις 14
Η μέθοδος equals() της κλάσης Object Όταν θέλουμε να ελέγξουμε ότι δύο μεταβλητές πρωτογενών τύπων είναι ίσες γράφουμε: int i, j; if (i == j). Για να συγκρίνουμε τα περιεχόμενα δύο αντικειμένων χρησιμοποιούμε μ τη ημέθοδο equals() της κλάσης Object: Object x, y; // ή Point x, y if (x.equals(y)). 15