====== Functional Interfaces ====== Τα Functional Interfaces είναι ένα από τα νεότερα χαρακτηριστικά που προστέθηκαν στη Java από την έκδοση 8 και μετά. Αποτελούν απαραίτητα στοιχεία για την γραφή προγραμμάτων με μία μη-αντικειμενοστραφή (συναρτησιακή λογική) σε συνδυασμό με τα //lamda expressions//, την οποία θα δούμε στη συνέχεια. ===== Τι είναι τα Functional Interfaces ; ===== Ένα Functional Interface είναι ένα interface το οποίο περιέχει ακριβώς μία abstract (αφηρημένη) μέθοδο. Μπορεί να έχει επιπλέον //default// ή //static// μεθόδους τις οποίες όμως δεν υπολογίζουμε, διότι έχουν ήδη κάποια υλοποίηση. Συνήθως, φέρει την προαιρετική σήμανση (annotation) @FunctionalInterface πριν από τη δήλωση του Functional Interface. Αυτή η σήμανση ενημερώνει τον compiler να βγάλει σφάλμα αν κατά λάθος προσθέσουμε και δεύτερη αφηρημένη μέθοδο. Γνωστό παράδειγμα, functional interface είναι το [[https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html|java.util.Comparator]] που λειτουργεί ως συγκριτής και επιτρέπει τη σύγκριση δύο αντικειμένων μεταξύ τους. ==== Παράδειγμα χρήσης του Functional Interface java.util.Comparator ==== Ας υποθέσουμε ότι διαθέτουμε την κλάση [[java:jfc_interfaces|Student]] (κατεβάστε τον κώδικα) που περιέχει δύο //private// πεδία τα όνομα και το επίθετο του φοιτητή και έχουμε μία λίστα φοιτητών που θέλουμε να την ταξινομήσουμε με τη βοήθεια της μεθόδου [[https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#sort-java.util.List-java.util.Comparator-|sort]] της κλάσης [[https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html|java.util.Collections]]. Θα ταξινομήσουμε τους φοιτητές με βάση το επίθετο τους, με τη βοήθεια του παρακάτω συγκριτή: public class StudentComparator implements java.util.Compator { int compare(Student a, Student b) { return a.getLastName().compareTo(b.getLastName()); } } import java.util.Comparator; import java.util.Arrays; import java.util.Collections; import java.util.List; public class StudentSorter { public static void main(String[] args) { List list = Arrays.asList( new Student("Minnie", "Mouse"), new Student("Mickey", "Gouse"), new Student("Marry", "Poppins"), new Student("Peter", "Pan") ); System.out.println(list); Collections.sort(list, new StudentComparator()) ); System.out.println(list); } } Παρατηρήστε ότι για να ταξινομήσουμε τα αντικείμενα πρέπει να κατασκευάσουμε την κλάση //StudentComparator// και στη συνέχεια να φτιάξουμε ένα αντικείμενο της κλάσης αυτής. Θα μπορούσαμε να αποφύγουμε τη σύνταξη του κώδικα της κλάσης StudentComparator και τη δημιουργία του αντικειμένου με χρήση ενός //lamda expression// ως εξής: import java.util.Comparator; import java.util.Arrays; import java.util.Collections; import java.util.List; public class StudentSorter { public static void main(String[] args) { List list = Arrays.asList( new Student("Minnie", "Mouse"), new Student("Mickey", "Gouse"), new Student("Marry", "Poppins"), new Student("Peter", "Pan") ); System.out.println(list); Collections.sort(list, (a,b) -> a.getLastName().compareTo(b.getLastName()) ); System.out.println(list); } } H έκφραση ''(a,b) -> a.getLastName().compareTo(b.getLastName())'' είναι ένα //lamda expression//. Μπορείτε να σκέφτεσε το //lamda expression// ως μία ανώνυμη συνάρτηση, που αποτελεί την υλοποίηση της μοναδικής //abstract// μεθόδου του αντίστοιχου Functional Interface. Ο λόγος που επιλέγεται η νέα σύνταξη είναι η αποφυγή κατασκευής επιπλέον κλάσεων, ενώ ο κώδικας γίνεται πιο περιγραφικός, διότι η λογική της σύγκρισης τοποθετείται στο σημείο χρήσης της μεθόδου. Περισσότερα για τα //lamda expressions// θα δούμε στη συνέχεια. ==== Παράδειγμα ορισμού ενός δικού μας Functional Interface ==== Ας υποθέσουμε ότι έχουμε το interface CustomHasher, το οποίο παρέχει την υλοποίση ενός hash function για κάθε διαφορετικό τύπο δεδομένων T, όπως αυτό περιγράφεται παρακάτω: @FunctionalInterface public interface CustomHasher { public long hash(T t); } Ας υποθέσουμε τώρα ότι για την παραπάνω κλάση [[java:jfc_interfaces|Student]] του φοιτητή υλοποιούμε τον παρακάτω CustomHasher: public class StudentHasher implements CustomHasher { public long hash(Student s) { long value = 5381; for (char c : s.getLastName().toCharArray()) { value = ((value << 5) + value) + (int)c; } for (char c : s.getFirstName().toCharArray()) { value = ((value << 5) + value) + (int)c; } return value; } } O κώδικας για να μετασχηματιστεί σε hash value ένας φοιτητής δίνεται παρακάτω public class StudentHasher { public static void main(String[] args) { Student mickey = new Student("Mickey", "Mouse"); StudentHasher sh = new StudentHasher(); System.out.println("Hash is: "+sh.hash(mickey)); } } Ο παραπάνω κώδικας θα μπορούσε να γραφεί ως εξής, με τη βοήθεια ενος //lamda expression// : { public class HashStudent { public static void main(String[] args) { Student mickey = new Student("Mickey", "Mouse"); CustomHasher hs = (s) -> { long value = 5381; for (char c : s.getLastName().toCharArray()) { value = ((value << 3) + value) + (int)c; } for (char c : s.getFirstName().toCharArray()) { value = ((value << 3) + value) + (int)c; } return value; }; System.out.println("Hash is: "+ hs.hash(mickey) ); } } }