====== 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) );
}
}
}