User Tools

Site Tools


java:anon_inner_classes

This is an old revision of the document!


Ανώνυμες εμφωλευμένες κλάσεις

Οι ανώνυμές εμφωλευμένες κλάσεις επιτρέπουν τον ταυτόχρονο ορισμό της κλάσης και την δημιουργία ενός αντικειμένου, σε μία εντολή. Οι κλάσεις αυτές ΠΡΕΠΕΙ α) να υλοποιούν ένα υφιστάμενο interface ή β) να επεκτείνουν μία υφιστάμενη κλάση.

Ανώνυμες εμφωλευμένες κλάσεις μπορούμε να υλοποιήσουμε όταν συντρέχουν ταυτόχρονα οι παρακάτω λόγοι:

  1. επιθυμούμε την υλοποίηση εσωτερικής κλάσης.
  2. η κλάση αυτή υλοποιεί ένα interface ή επεκτείνει μία άλλη κλάση (η κλάση που επεκτείνεται μπορεί να είναι abstract ή όχι) και
  3. από την κλάση αυτή πρόκειται να δημιουργηθεί μόνο ένα αντικείμενο.

Σε αυτές τις περιπτώσεις η χρήση ανώνυμων κλάσεων μπορεί να είναι βολική, καθώς συμπιέζει τον όγκο του κώδικα. Από την άλλη πλευρά η εκτεταμένη χρήση ανώνυμων κλάσεων μπορεί να δημιουργήσει κώδικα που είναι δυσανάγνωστος.

Παράδειγμα - Ανώνυμη εμφωλευμένη κλάση ως υλοποίηση ενός interface

Στο παρακάτω παράδειγμα ορίζουμε την κλάση Student η οποία έχει τα τρία (3) πεδία firstName, lastName, AEM. Στη μέθοδο main ορίζουμε μία λίστα (ArrayList) από δεδομένα τύπου Student τα οποία διαβάζουμε από το αρχείο students.txt. Θέλουμε να ταξινομήσουμε τα δεδομένα της λίστας κατά αύξουσα σειρά με δύο διαφορετικούς τρόπους α)με βάση τη σειρά επώνυμο/όνομα/ΑΕΜ και β) με βάση τη σειρά ΑΕΜ/επώνυμο/όνομα. Η ταξινόμηση των στοιχείων μίας λίστας μπορεί να γίνει με τη βοήθεια της στατικής μεθόδου Collections.sort(java.util.List, java.util.Comparator), όπου παρέχεται ένα συγκριτής (comparator) για την σύγκριση των στοιχείων της λίστας. Η σύγκριση έχει υλοποιηθεί με δύο ανώνυμες κλάσεις που υλοποιούν και οι δύο το interface java.util.Comparator.

Student.java
import java.util.Comparator;
import java.util.*;
import java.io.*;
 
class Student {
  String firstName;
  String lastName;
  int AEM;
 
  public Student(int AEM, String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.AEM = AEM;
  }
 
  public String toString() {
    return String.format("%5d. %s, %s", AEM, lastName, firstName);
  }
 
  Comparator<Student> lexicographicCmp = new Comparator<Student>() {
    public int compare(Student st1, Student st2) {
      int cmp = st1.lastName.compareTo(st2.lastName);
      if(cmp==0)
        cmp = st1.firstName.compareTo(st2.firstName);
      if(cmp==0)
        cmp = st1.AEM - st2.AEM;
      return cmp;
    }
    public boolean equals(Object o) {
      if(o instanceof Student) {
        Student st = (Student) o;
        if(firstName.equals(st.firstName) && lastName.equals(st.lastName) && AEM == st.AEM)
          return true;
      }
      return false;
    }
  };
 
  public static void readFromFile(List<Student> students, File f) {    
    try(Scanner sc = new Scanner(f)) {
      while(sc.hasNextLine()) {        
        students.add( new Student(sc.nextInt(), sc.next(), sc.next()) );
      }
    }
    catch(FileNotFoundException ex) {
      System.out.println("File \""+f.getName()+"\" was not found!");
    }
    catch(InputMismatchException ex) {
      System.out.println("Input mismatch while reading with scanner!!!");
      ex.printStackTrace();
      System.exit(-1);
    }
  }
 
  public void sortLexicographically(List<Student> students) {    
    Collections.sort(students, lexicographicCmp);
  }
 
  public void sortByAEM(List<Student> students) {
    Collections.sort(students, new Comparator<Student>() {
        public int compare(Student st1, Student st2) {
          int cmp = cmp = st1.AEM - st2.AEM;
          if(cmp==0)
            cmp = st1.lastName.compareTo(st2.lastName);            
          if(cmp==0)
            cmp = st1.firstName.compareTo(st2.firstName);
          return cmp;
        }
        public boolean equals(Object o) {
          if(o instanceof Student) {
            Student st = (Student) o;
            if(firstName.equals(st.firstName) && lastName.equals(st.lastName) && AEM == st.AEM)
              return true;
          }
          return false;
        }
      }
    );
  }
 
  public static String toString(List<Student> students) {
    StringBuffer str = new StringBuffer();
    for(Student st : students) 
      str.append(st+"\n");
    str.append("---------------------------------\n");
    return str.toString();
  }
 
  public static void main(String []args) {
    if(args.length == 0) {
      System.out.println("Insufficient number of arguments!\n");
      return;
    }
    List<Student> students = new ArrayList<Student>();
    readFromFile(students, new File(args[0]));
    System.out.println(" --- Init Ordering ---\n");
    System.out.println(toString(students));
    students.get(0).sortLexicographically(students);
    System.out.println(" --- Lexicographic Ordering ---\n");
    System.out.println(toString(students));
    students.get(0).sortByAEM(students);
    System.out.println(" --- AEM Ordering ---\n");
    System.out.println(toString(students));
  }
 
}

Παράδειγμα - Ανώνυμη εμφωλευμένη κλάση ως επέκταση υφιστάμενης κλάσης

Παρακάτω δίνεται ένα άλλο παράδειγμα, όπου η ανώνυμη κλάση επεκτείνει μία υφιστάμενη κλάση αντί για ένα inteface.

HelloWorldAnonymousClasses.java
public class HelloWorldAnonymousClasses {
 
  interface HelloWorld {
    public void greet();
    public void greetSomeone(String someone);
  }
 
  public static void sayHello() {
 
    class EnglishGreeting implements HelloWorld {
      String name = "world";
      public void greet() {
        greetSomeone("world");
      }
      public void greetSomeone(String someone) {
        name = someone;
        System.out.println("Hello " + name);
      }
    }
 
    HelloWorld englishGreeting = new EnglishGreeting();
 
    HelloWorld frenchGreeting = new HelloWorld() {
      String name = "tout le monde";
      public void greet() {
        greetSomeone("tout le monde");
      }
      public void greetSomeone(String someone) {
        name = someone;
        System.out.println("Salut " + name);
      }
    };
 
    HelloWorld spanishGreeting = new EnglishGreeting() {
      String name = "mundo";
      public void greet() {
        greetSomeone("mundo");
      }
      public void greetSomeone(String someone) {
        name = someone;
        System.out.println("Hola, " + name);
      }
    };
    englishGreeting.greet();
    frenchGreeting.greetSomeone("Fred");
    spanishGreeting.greet();
  }
 
  public static void main(String... args) {    
    HelloWorldAnonymousClasses.sayHello();
  }      
}

Παρατηρήστε ότι η μεταβλητή frenchGreeting προκύπτει από μία κλάση που υλοποιεί το inteface HelloWorld η μεταβλητή spanishGreeting προκύπτει ως επέκταση της κλάση EnglishGreeting.

Πρόσβαση στις τοπικές μεταβλητές και στις μεταβλητές της εξωτερικής κλάσης

Σε αναλογία με τις τοπικές εμφωλευμένες κλάσεις οι ανώνυμες κλάσεις έχουν τις ίδιες δυνατότητες πρόσβασης στις τοπικές μεταβλήτές της μεθόδου στην οποία ορίζονται:

  • Η ανώνυμη κλάση έχει πρόσβαση στις μεταβλητές της εξωτερικής κλάσης.
  • Η ανώνυμη κλάση δεν μπορεί να έχει πρόσβαση σε τοπικές μεταβλητές της μεθόδου στην οποία ορίζονται αν αυτές δεν έχουν δηλωθεί ως final.
  • Όπως σε όλες τις μη στατικές εμφωλευμένες κλάσεις ο ορισμός του ίδιου τύπου δεδομένων στην εσωτερική και την εξωτερική κλάση “κρύβει” τον συγκεκριμένο τύπο δεδομένων από την εξωτερική κλάση.
  • Δεν μπορείτε να ορίσετε στατικές μεταβλητές ή μεθόδους μέσα σε μία ανώνυμη κλάση.

Σε μία ανώνυμη κλάση μπορείτε να ορίσετε τα εξής:

  • Πεδία
  • Επιπλέον μεθόδους που δεν ορίζονται στη interface ή στην κλάση που επεκτείνει.
  • Μεθόδους ή block αρχικοποίησης πεδίων της κλάσης.
  • Τοπικές κλάσεις που ανήκουν στη ανώνυμη κλάση.
  • ΔΕΝ μπορείτε και δεν έχει νόημα να ορίσετε κατασκευαστές μέσα σε μία ανώνυμη κλάση.
java/anon_inner_classes.1521624703.txt.gz · Last modified: 2018/03/21 09:31 (external edit)