Table of Contents
Functional Interfaces
Τα Functional Interfaces είναι ένα από τα νεότερα χαρακτηριστικά που προστέθηκαν στη Java από την έκδοση 8 και μετά. Αποτελούν απαραίτητα στοιχεία για την γραφή προγραμμάτων με μία μη-αντικειμενοστραφή (συναρτησιακή λογική) σε συνδυασμό με τα lamda expressions, την οποία θα δούμε στη συνέχεια.
Τι είναι τα Functional Interfaces ;
Ένα Functional Interface είναι ένα interface το οποίο περιέχει ακριβώς μία abstract (αφηρημένη) μέθοδο. Μπορεί να έχει επιπλέον default ή static μεθόδους τις οποίες όμως δεν υπολογίζουμε, διότι έχουν ήδη κάποια υλοποίηση.
Συνήθως, φέρει την προαιρετική σήμανση (annotation) @FunctionalInterface πριν από τη δήλωση του Functional Interface. Αυτή η σήμανση ενημερώνει τον compiler να βγάλει σφάλμα αν κατά λάθος προσθέσουμε και δεύτερη αφηρημένη μέθοδο.
Γνωστό παράδειγμα, functional interface είναι το java.util.Comparator που λειτουργεί ως συγκριτής και επιτρέπει τη σύγκριση δύο αντικειμένων μεταξύ τους.
Παράδειγμα χρήσης του Functional Interface java.util.Comparator
Ας υποθέσουμε ότι διαθέτουμε την κλάση Student (κατεβάστε τον κώδικα) που περιέχει δύο private πεδία τα όνομα και το επίθετο του φοιτητή και έχουμε μία λίστα φοιτητών που θέλουμε να την ταξινομήσουμε με τη βοήθεια της μεθόδου sort της κλάσης java.util.Collections.
Θα ταξινομήσουμε τους φοιτητές με βάση το επίθετο τους, με τη βοήθεια του παρακάτω συγκριτή:
- StudentComparator.java
public class StudentComparator<Student> implements java.util.Compator<Student> { int compare(Student a, Student b) { return a.getLastName().compareTo(b.getLastName()); } }
- SortStudents.java
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<Student> 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 ως εξής:
- SortStudents.java
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<Student> 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<T>, το οποίο παρέχει την υλοποίση ενός hash function για κάθε διαφορετικό τύπο δεδομένων T, όπως αυτό περιγράφεται παρακάτω:
- CustomHasher.java
@FunctionalInterface public interface CustomHasher<T> { public long hash(T t); }
Ας υποθέσουμε τώρα ότι για την παραπάνω κλάση Student του φοιτητή υλοποιούμε τον παρακάτω CustomHasher:
- StudentHasher.java
public class StudentHasher implements CustomHasher<Student> { 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 ένας φοιτητής δίνεται παρακάτω
- HashStudent.java
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 :
- HashStudent.java
{ public class HashStudent { public static void main(String[] args) { Student mickey = new Student("Mickey", "Mouse"); CustomHasher<Student> 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) ); } } }
