Προγραμματισμός

Λειτουργικός προγραμματισμός για προγραμματιστές Java, Μέρος 2

Καλώς ήλθατε πίσω σε αυτό το σεμινάριο δύο μερών που παρουσιάζει λειτουργικό προγραμματισμό σε περιβάλλον Java. Στο Λειτουργικό προγραμματισμό για προγραμματιστές Java, Μέρος 1, χρησιμοποίησα παραδείγματα JavaScript για να ξεκινήσω με πέντε τεχνικές λειτουργικού προγραμματισμού: καθαρές συναρτήσεις, συναρτήσεις υψηλότερης τάξης, τεμπέλης αξιολόγησης, κλείσιμο και κάρι. Η παρουσίαση αυτών των παραδειγμάτων σε JavaScript μας επέτρεψε να επικεντρωθούμε στις τεχνικές με μια απλούστερη σύνταξη, χωρίς να μπείτε στις πιο περίπλοκες λειτουργικές δυνατότητες προγραμματισμού της Java.

Στο Μέρος 2 θα επανεξετάσουμε αυτές τις τεχνικές χρησιμοποιώντας κώδικα Java που προηγείται της Java 8. Όπως θα δείτε, αυτός ο κώδικας είναι λειτουργικός, αλλά δεν είναι εύκολο να γράψετε ή να διαβάσετε. Θα γνωρίσετε επίσης τις νέες λειτουργικές δυνατότητες προγραμματισμού που ενσωματώθηκαν πλήρως στη γλώσσα Java στο Java 8. δηλαδή, lambdas, αναφορές μεθόδων, λειτουργικές διεπαφές και το API ροών.

Σε όλο αυτό το σεμινάριο θα επανεξετάσουμε παραδείγματα από το Μέρος 1 για να δούμε πώς συγκρίνονται τα παραδείγματα JavaScript και Java. Θα δείτε επίσης τι συμβαίνει όταν ενημερώνω μερικά από τα παραδείγματα προ της Java 8 με λειτουργικές λειτουργικές γλώσσες όπως λάμδα και αναφορές μεθόδων. Τέλος, αυτό το σεμινάριο περιλαμβάνει μια πρακτική άσκηση σχεδιασμένη για να σας βοηθήσει πρακτική λειτουργική σκέψη, το οποίο θα κάνετε μετατρέποντας ένα κομμάτι αντικειμενοστρεφούς κώδικα Java σε λειτουργικό ισοδύναμο.

λήψη Λήψη του κώδικα Λήψη του πηγαίου κώδικα για παράδειγμα εφαρμογές σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Jeff Friesen για το JavaWorld.

Λειτουργικός προγραμματισμός με Java

Πολλοί προγραμματιστές δεν το συνειδητοποιούν, αλλά ήταν δυνατό να γράψουμε λειτουργικά προγράμματα στην Java πριν από την Java 8. Για να έχουμε μια ολοκληρωμένη εικόνα του λειτουργικού προγραμματισμού στην Java, ας αναθεωρήσουμε γρήγορα λειτουργικές λειτουργίες προγραμματισμού που προηγούνται της Java 8. Μόλις το κάνετε Τα έχετε κατεβάσει, πιθανότατα θα έχετε μεγαλύτερη εκτίμηση για το πώς νέες δυνατότητες που εισήχθησαν στο Java 8 (όπως lambda και λειτουργικές διεπαφές) απλοποίησαν την προσέγγιση της Java στον λειτουργικό προγραμματισμό.

Όρια της υποστήριξης της Java για λειτουργικό προγραμματισμό

Ακόμη και με λειτουργικές βελτιώσεις προγραμματισμού στο Java 8, η Java παραμένει επιτακτική, αντικειμενοστραφής γλώσσα προγραμματισμού. Λείπει τύποι εύρους και άλλες δυνατότητες που θα το καθιστούσαν πιο λειτουργικό. Η Java ασχολείται επίσης με την ονομαστική πληκτρολόγηση, που είναι η προϋπόθεση ότι κάθε τύπος πρέπει να έχει όνομα. Παρά αυτούς τους περιορισμούς, οι προγραμματιστές που αγκαλιάζουν τα λειτουργικά χαρακτηριστικά της Java εξακολουθούν να επωφελούνται από το να μπορούν να γράφουν πιο περιεκτικό, επαναχρησιμοποιήσιμο και αναγνώσιμο κώδικα.

Λειτουργικός προγραμματισμός πριν από την Java 8

Οι ανώνυμες εσωτερικές τάξεις μαζί με διεπαφές και κλείσιμο είναι τρία παλαιότερα χαρακτηριστικά που υποστηρίζουν λειτουργικό προγραμματισμό σε παλαιότερες εκδόσεις της Java:

  • Ανώνυμες εσωτερικές τάξεις σας επιτρέπει να μεταβιβάσετε τη λειτουργικότητα (που περιγράφεται από διεπαφές) σε μεθόδους.
  • Λειτουργικές διεπαφές είναι διασυνδέσεις που περιγράφουν μια συνάρτηση.
  • Κλείσιμο σας επιτρέπουν να έχετε πρόσβαση σε μεταβλητές στα εξωτερικά τους πεδία.

Στις ενότητες που ακολουθούν θα επανεξετάσουμε τις πέντε τεχνικές που παρουσιάζονται στο Μέρος 1, αλλά χρησιμοποιώντας τη σύνταξη Java. Θα δείτε πώς ήταν δυνατή η κάθε μια από αυτές τις λειτουργικές τεχνικές πριν από το Java 8.

Γράφοντας καθαρές λειτουργίες στην Java

Η λίστα 1 παρουσιάζει τον πηγαίο κώδικα σε ένα παράδειγμα εφαρμογής, DaysInMonth, που γράφεται χρησιμοποιώντας μια ανώνυμη εσωτερική κλάση και μια λειτουργική διεπαφή. Αυτή η εφαρμογή δείχνει πώς να γράψετε μια καθαρή συνάρτηση, η οποία ήταν εφικτή στην Java πολύ πριν από την Java 8.

Λίστα 1. Μια καθαρή συνάρτηση στην Java (DaysInMonth.java)

Λειτουργία διεπαφής {R ισχύει (T t); } δημόσια τάξη DaysInMonth {public static void main (String [] args) {Function dim = new Function () {@Override public Integer apply (Integer month) {return new Integer [] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} [μήνας] }} System.out.printf ("Απρίλιος:% d% n", dim.apply (3)); System.out.printf ("Αύγουστος:% d% n", dim.apply (7)); }}

Το γενικό Λειτουργία διεπαφή στην καταχώριση 1 περιγράφει μια συνάρτηση με μία μόνο παράμετρο τύπου Τ και έναν τύπο επιστροφής τύπου Ρ. ο Λειτουργία διεπαφή δηλώνει ένα R ισχύουν (T t) μέθοδος που εφαρμόζει αυτήν τη συνάρτηση στο δεδομένο όρισμα.

ο κύριος() Η μέθοδος δημιουργεί μια ανώνυμη εσωτερική τάξη που εφαρμόζει το Λειτουργία διεπαφή. ο ισχύουν() μέθοδος unboxes μήνας και το χρησιμοποιεί για την ευρετηρίαση μιας σειράς ακέραιων ημερών σε μήνα. Ο ακέραιος αριθμός σε αυτό το ευρετήριο επιστρέφεται. (Αγνοώ άλματα χρόνια για απλότητα.)

κύριος() Στη συνέχεια εκτελεί αυτήν τη λειτουργία δύο φορές με την επίκληση ισχύουν() για να επιστρέψετε τις μετρήσεις της ημέρας για τους μήνες Απρίλιο και Αύγουστο. Αυτές οι μετρήσεις εκτυπώνονται στη συνέχεια.

Καταφέραμε να δημιουργήσουμε μια συνάρτηση, και μια καθαρή συνάρτηση σε αυτό! Θυμηθείτε ότι α καθαρή λειτουργία εξαρτάται μόνο από τα επιχειρήματά του και από καμία εξωτερική κατάσταση. Δεν υπάρχουν παρενέργειες.

Μεταγλώττιση καταχώρισης 1 ως εξής:

javac DaysInMonth.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

java DaysInMonth

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

Απρίλιος: 30 Αυγούστου: 31

Γράφοντας συναρτήσεις υψηλότερης τάξης στην Java

Στη συνέχεια, θα εξετάσουμε συναρτήσεις υψηλότερης τάξης, γνωστές και ως λειτουργίες πρώτης κατηγορίας. Θυμηθείτε ότι α συνάρτηση υψηλότερης τάξης λαμβάνει ορίσματα συνάρτησης και / ή επιστρέφει ένα αποτέλεσμα συνάρτησης. Η Java συσχετίζει μια συνάρτηση με μια μέθοδο, η οποία ορίζεται σε μια ανώνυμη εσωτερική κλάση. Μια παρουσία αυτής της κλάσης μεταβιβάζεται ή επιστρέφεται από άλλη μέθοδο Java που χρησιμεύει ως συνάρτηση υψηλότερης τάξης. Το ακόλουθο κομμάτι κώδικα προσανατολισμένο σε αρχεία δείχνει τη μετάδοση μιας συνάρτησης σε μια λειτουργία υψηλότερης τάξης:

File [] txtFiles = new File ("."). ListFiles (new FileFilter () {@Override public boolean accept (Αρχείο pathname) {return pathname.getAbsolutePath (). Berakhir με ("txt");}});

Αυτό το τμήμα κώδικα περνά μια συνάρτηση που βασίζεται στο java.io.FileFilter λειτουργική διεπαφή με το java.io. Αρχείο της τάξης File [] listFiles (φίλτρο FileFilter) μέθοδο, λέγοντάς του να επιστρέψει μόνο εκείνα τα αρχεία με κείμενο επεκτάσεις.

Η λίστα 2 δείχνει έναν άλλο τρόπο εργασίας με λειτουργίες υψηλότερης τάξης στην Java. Σε αυτήν την περίπτωση, ο κώδικας μεταδίδει μια συνάρτηση σύγκρισης σε ένα είδος() συνάρτηση υψηλότερης τάξης για ένα είδος αύξουσας τάξης και μια δεύτερη συνάρτηση σύγκρισης είδος() για είδος φθίνουσας τάξης.

Λίστα 2. Μια συνάρτηση υψηλότερης τάξης στην Java (Sort.java)

εισαγωγή java.util.Comparator; δημόσια τάξη Ταξινόμηση {public static void main (String [] args) {String [] innerplanets = {"Mercury", "Venus", "Earth", "Mars"}; χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, new Comparator () {@ Override public int membandingkan (String e1, String e2) {return e1.compareTo (e2);}}); χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, new Comparator () {@ Override public int membandingkan (String e1, String e2) {return e2.compareTo (e1);}}); χωματερή (εσωτερικά πλανήτες) } στατική κενή απόρριψη (συστοιχία T []) {για (στοιχείο T: array) System.out.println (στοιχείο); System.out.println (); } στατικό άκυρο είδος (πίνακας T [], Συγκριτικός cmp) {για (int pass = 0; pass  πέρασμα; i--) if (cmp.compare (array [i], array [pass]) <0) ανταλλαγή (array, i, pass); } στατική ανταλλαγή κενού (T [] πίνακας, int i, int j) {T temp = array [i]; πίνακας [i] = πίνακας [j]; πίνακας [j] = temp; }}

Η λίστα 2 εισάγει το java.util. Συγκριτής λειτουργική διεπαφή, η οποία περιγράφει μια λειτουργία που μπορεί να πραγματοποιήσει μια σύγκριση σε δύο αντικείμενα αυθαίρετου αλλά ταυτόσημου τύπου.

Δύο σημαντικά μέρη αυτού του κώδικα είναι το είδος() μέθοδος (η οποία εφαρμόζει τον αλγόριθμο Bubble Sort) και το είδος() επίκληση στο κύριος() μέθοδος. Παρόλο είδος() απέχει πολύ από το να είναι λειτουργικό, δείχνει μια συνάρτηση υψηλότερης τάξης που λαμβάνει μια συνάρτηση - το συγκριτικό - ως επιχείρημα. Εκτελεί αυτή τη λειτουργία επικαλούμενος την συγκρίνω() μέθοδος. Δύο περιπτώσεις αυτής της συνάρτησης περνούν σε δύο είδος() καλεί μέσα κύριος().

Συγκεντρώστε την καταχώριση 2 ως εξής:

javac Sort.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

Ταξινόμηση java

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

Ερμής Αφροδίτη Γη Άρης Γη Άρης Ερμή Αφροδίτη Αφροδίτη Γη Ερμή Γη Άρης

Τεμπέλη αξιολόγηση στην Java

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

  • Το Boolean && και || τελεστές, οι οποίοι δεν θα αξιολογήσουν το δεξί τελεστή τους όταν ο αριστερός τελεστής είναι ψευδής (&&) ή αλήθεια (||).
  • ο ?: τελεστής, ο οποίος αξιολογεί μια έκφραση Boolean και στη συνέχεια αξιολογεί μόνο μία από τις δύο εναλλακτικές εκφράσεις (συμβατού τύπου) με βάση την πραγματική / λανθασμένη τιμή της έκφρασης Boolean.

Ο λειτουργικός προγραμματισμός ενθαρρύνει τον προγραμματισμό προσανατολισμένο στην έκφραση, οπότε θα θέλετε να αποφύγετε τη χρήση δηλώσεων όσο το δυνατόν περισσότερο. Για παράδειγμα, ας υποθέσουμε ότι θέλετε να αντικαταστήσετε τα Java αν-αλλού δήλωση με ένα ifThenElse () μέθοδος. Η λίστα 3 δείχνει μια πρώτη απόπειρα.

Λίστα 3. Ένα παράδειγμα πρόθυμης αξιολόγησης στην Java (EagerEval.java)

δημόσια τάξη EagerEval {public static void main (String [] args) {System.out.printf ("% d% n", ifThenElse (true, square (4), cube (4))); System.out.printf ("% d% n", ifThenElse (false, τετράγωνο (4), κύβος (4))); } στατικό int cube (int x) {System.out.println ("in cube"); επιστροφή x * x * x; } στατικό int ifThenElse (boolean predicate, int onTrue, int onFalse) {return (predicate); onTrue: onFalse; } στατικό int τετράγωνο (int x) {System.out.println ("in square"); επιστροφή x * x; }}

Η λίστα 3 ορίζει ένα ifThenElse () μέθοδος που παίρνει ένα Boolean predicate και ένα ζευγάρι ακέραιων, επιστρέφοντας το στο αληθινό ακέραιος όταν το κατηγορηματικό είναι αληθής και το σε λάθος ακέραιος διαφορετικά.

Η λίστα 3 ορίζει επίσης κύβος() και τετράγωνο() μεθόδους. Αντίστοιχα, αυτές οι μέθοδοι κυβίζουν και τετραγώνουν έναν ακέραιο και επιστρέφουν το αποτέλεσμα.

ο κύριος() επικαλείται μέθοδο ifThenElse (true, square (4), κύβος (4)), το οποίο πρέπει να επικαλείται μόνο τετράγωνο (4), ακολουθούμενη από ifThenElse (ψεύτικο, τετράγωνο (4), κύβος (4)), το οποίο πρέπει να επικαλείται μόνο κύβος (4).

Συγκεντρώστε την καταχώριση 3 ως εξής:

javac EagerEval.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

java EagerEval

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

στην πλατεία στον κύβο 16 στην πλατεία στον κύβο 64

Η έξοδος δείχνει ότι το καθένα ifThenElse () αποτελέσματα κλήσης και στις δύο μεθόδους εκτέλεσης, ανεξάρτητα από την έκφραση Boolean. Δεν μπορούμε να αξιοποιήσουμε το ?: τεμπελιά του χειριστή επειδή η Java αξιολογεί με ανυπομονησία τα επιχειρήματα της μεθόδου.

Αν και δεν υπάρχει τρόπος να αποφύγουμε την πρόθυμη αξιολόγηση των επιχειρημάτων μεθόδων, μπορούμε ακόμα να επωφεληθούμε ?:Οκνηρή αξιολόγηση για να διασφαλιστεί ότι μόνο τετράγωνο() ή κύβος() λέγεται. Η λίστα 4 δείχνει πώς.

Λίστα 4. Ένα παράδειγμα τεμπέλης αξιολόγησης στην Java (LazyEval.java)

Λειτουργία διεπαφής {R ισχύει (T t); } δημόσια τάξη LazyEval {public static void main (String [] args) {Function square = new Function () {{System.out.println ("SQUARE"); } @Override public Integer apply (Integer t) {System.out.println ("in square"); επιστροφή t * t; }} Function cube = new Function () {{System.out.println ("CUBE"); } @Override public Integer apply (Integer t) {System.out.println ("in cube"); επιστροφή t * t * t; }} System.out.printf ("% d% n", ifThenElse (true, square, cube, 4)); System.out.printf ("% d% n", ifThenElse (false, square, cube, 4)); } στατικό R ifThenElse (boolean predicate, Function onTrue, Function onFalse, T t) {return (predicate? onTrue.apply (t): onFalse.apply (t)); }}

Λίστα 4 στροφών ifThenElse () σε μια λειτουργία υψηλότερης τάξης, δηλώνοντας ότι αυτή η μέθοδος λαμβάνει ένα ζευγάρι Λειτουργία επιχειρήματα. Αν και αυτά τα επιχειρήματα αξιολογούνται με ανυπομονησία όταν διαβιβάζονται ifThenElse (), ο ?: Ο χειριστής προκαλεί την εκτέλεση μόνο μιας από αυτές τις λειτουργίες (μέσω ισχύουν()). Μπορείτε να δείτε τόσο ανυπόμονη όσο και τεμπέλης αξιολόγηση στην εργασία κατά τη σύνταξη και εκτέλεση της εφαρμογής.

Συγκεντρώστε την καταχώριση 4 ως εξής:

javac LazyEval.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

java LazyEval

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

ΠΛΑΤΕΙΑΚΟΣ ΚΥΒΟΣ στην πλατεία 16 στον κύβο 64

Ένας τεμπέλης επαναληπτικός και πολλά άλλα

Το «Τεμπελιά, Μέρος 1: Εξερεύνηση της τεμπέλης αξιολόγησης στην Ιάβα» της Neal Ford παρέχει περισσότερες πληροφορίες για την τεμπέληνη αξιολόγηση. Ο συγγραφέας παρουσιάζει έναν τεμπέλης επαναληπτικό Java που βασίζεται σε Java μαζί με μερικά τετραπλασιασμένα Java.

Κλείσιμο σε Java

Μια ανώνυμη παρουσία εσωτερικής τάξης σχετίζεται με το a κλείσιμο. Οι μεταβλητές εξωτερικού πεδίου πρέπει να δηλωθούν τελικός ή (ξεκινώντας από την Java 8) αποτελεσματικά τελικό (που σημαίνει ότι δεν έχει τροποποιηθεί μετά την προετοιμασία) για να είναι προσβάσιμο. Εξετάστε την καταχώριση 5.

Λίστα 5. Ένα παράδειγμα κλεισίματος στην Java (PartialAdd.java)

Λειτουργία διεπαφής {R ισχύει (T t); } δημόσια τάξη PartialAdd {Function add (final int x) {Function partialAdd = new Function () {@ Override public Integer apply (Integer y) {return y + x; }} επιστροφή partialAdd; } public static void main (String [] args) {PartialAdd pa = νέο PartialAdd (); Λειτουργία add10 = pa.add (10); Λειτουργία add20 = pa.add (20); System.out.println (add10.apply (5)); System.out.println (add20.apply (5)); }}

Η Λίστα 5 είναι το ισοδύναμο Java του κλεισίματος που παρουσίασα προηγουμένως σε JavaScript (βλ. Μέρος 1, Καταχώριση 8). Αυτός ο κωδικός δηλώνει ένα Προσθήκη() συνάρτηση υψηλότερης τάξης που επιστρέφει μια συνάρτηση για την εκτέλεση μερικής εφαρμογής του Προσθήκη() λειτουργία. ο ισχύουν() Η μέθοδος αποκτά πρόσβαση σε μεταβλητή Χ στο εξωτερικό πεδίο της Προσθήκη(), το οποίο πρέπει να δηλωθεί τελικός πριν από την Java 8. Ο κώδικας συμπεριφέρεται σχεδόν ίδιο με το αντίστοιχο JavaScript.

Συγκεντρώστε την καταχώριση 5 ως εξής:

javac PartialAdd.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

java PartialAdd

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

15 25

Κάρι στην Ιάβα

Ίσως έχετε παρατηρήσει ότι το Μερική προσθήκη Στην καταχώριση 5 καταδεικνύεται κάτι παραπάνω από απλώς κλείσιμο. Δείχνει επίσης κάρι, ο οποίος είναι ένας τρόπος για να μεταφράσετε την αξιολόγηση μιας συνάρτησης πολλαπλών επιχειρημάτων στην αξιολόγηση μιας ισοδύναμης ακολουθίας συναρτήσεων με ένα όρισμα. Και τα δυο ετησίως (10) και ετησίως (20) Στην Λίστα 5 επιστρέφετε ένα κλείσιμο που καταγράφει έναν τελεστή (10 ή 20, αντίστοιχα) και μια συνάρτηση που εκτελεί την προσθήκη - ο δεύτερος τελεστής (5) περνά μέσω add10.εφαρμογή (5) ή add20.apply (5).

Το Currying μας επιτρέπει να αξιολογήσουμε τα ορίσματα συνάρτησης ένα κάθε φορά, δημιουργώντας μια νέα συνάρτηση με ένα λιγότερο όρισμα σε κάθε βήμα. Για παράδειγμα, στο Μερική προσθήκη εφαρμογή, κάνουμε την ακόλουθη συνάρτηση:

f (x, y) = x + y

Θα μπορούσαμε να εφαρμόσουμε και τα δύο επιχειρήματα ταυτόχρονα, αποδίδοντας τα εξής:

f (10, 5) = 10 + 5

Ωστόσο, με το currying, εφαρμόζουμε μόνο το πρώτο επιχείρημα, αποδίδοντας αυτό:

f (10, y) = g (y) = 10 + y

Έχουμε τώρα μία λειτουργία, σολ, αυτό απαιτεί μόνο ένα επιχείρημα. Αυτή είναι η συνάρτηση που θα αξιολογηθεί όταν καλούμε το ισχύουν() μέθοδος.

Μερική εφαρμογή, όχι μερική προσθήκη

Το όνομα Μερική προσθήκη σημαίνει μερική εφαρμογή απο Προσθήκη() λειτουργία. Δεν σημαίνει μερική προσθήκη. Το Currying αφορά την εκτέλεση μερικής εφαρμογής μιας συνάρτησης. Δεν έχει να κάνει με μερικούς υπολογισμούς.

Ίσως να μπερδευτείτε από τη χρήση της φράσης "μερική εφαρμογή", ειδικά επειδή είπα στο Μέρος 1 ότι το κάρι δεν είναι το ίδιο με μερική εφαρμογή, η οποία είναι η διαδικασία καθορισμού ορισμένων ορισμάτων σε μια συνάρτηση, παράγοντας μια άλλη συνάρτηση μικρότερης ποιότητας. Με μερική εφαρμογή, μπορείτε να δημιουργήσετε συναρτήσεις με περισσότερα από ένα ορίσματα, αλλά με currying, κάθε συνάρτηση πρέπει να έχει ακριβώς ένα όρισμα.

Η λίστα 5 παρουσιάζει ένα μικρό παράδειγμα κάρι με βάση Java πριν από την Java 8. Τώρα εξετάστε το CurriedCalc εφαρμογή στην λίστα 6.

Λίστα 6. Currying σε κώδικα Java (CurriedCalc.java)

Λειτουργία διεπαφής {R ισχύει (T t); } δημόσια τάξη CurriedCalc {public static void main (String [] args) {System.out.println (calc (1) .apply (2) .apply (3) .apply (4)); } στατική συνάρτηση> calc (τελικός ακέραιος α) {επιστροφή νέας συνάρτησης> () {@ Override δημόσια λειτουργία εφαρμογή (τελικός ακέραιος β) {επιστροφή νέας συνάρτησης() {@Override public Function apply (final Integer c) {return new Function () {@Override public Integer apply (Integer d) {return (a + b) * (c + d); }} }} }} }}

Η λίστα 6 χρησιμοποιεί currying για την αξιολόγηση της συνάρτησης f (a, b, c, d) = (a + b) * (c + d). Δίνεται έκφραση υπολ. (1). εφαρμογή (2). εφαρμογή (3). εφαρμογή (4), αυτή η συνάρτηση έχει ως εξής:

  1. f (1, b, c, d) = g (b, c, d) = (1 + b) * (c + d)
  2. g (2, c, d) = h (c, d) = (1 + 2) * (c + d)
  3. h (3, d) = i (d) = (1 + 2) * (3 + d)
  4. i (4) = (1 + 2) * (3 + 4)

Συλλογή καταχώρισης 6:

javac CurriedCalc.java

Εκτελέστε την εφαρμογή που προκύπτει:

java CurriedCalc

Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

21

Επειδή το currying αφορά την εκτέλεση μερικής εφαρμογής μιας συνάρτησης, δεν έχει σημασία με ποια σειρά εφαρμόζονται τα ορίσματα. Για παράδειγμα, αντί να περάσει ένα προς την υπολ. () και ρε στο πιο ένθετο ισχύουν() μέθοδος (που εκτελεί τον υπολογισμό), θα μπορούσαμε να αντιστρέψουμε αυτά τα ονόματα παραμέτρων. Αυτό θα είχε ως αποτέλεσμα δ γ β α αντί Α Β Γ Δ, αλλά θα επιτύχει το ίδιο αποτέλεσμα με 21. (Ο πηγαίος κώδικας για αυτό το σεμινάριο περιλαμβάνει την εναλλακτική έκδοση του CurriedCalc.)

Λειτουργικός προγραμματισμός στην Java 8

Ο λειτουργικός προγραμματισμός πριν από την Java 8 δεν είναι όμορφος. Απαιτείται πάρα πολύς κώδικας για τη δημιουργία, τη μετάδοση μιας συνάρτησης και / ή την επιστροφή μιας λειτουργίας από μια λειτουργία πρώτης κατηγορίας. Οι προηγούμενες εκδόσεις Java δεν διαθέτουν επίσης προκαθορισμένες λειτουργικές διεπαφές και λειτουργίες πρώτης κατηγορίας, όπως φίλτρο και χάρτη.

Το Java 8 μειώνει την ευγένεια σε μεγάλο βαθμό εισάγοντας λάμδα και αναφορές μεθόδων στη γλώσσα Java. Προσφέρει επίσης προκαθορισμένες λειτουργικές διεπαφές και καθιστά διαθέσιμα τα φίλτρα, το χάρτη, τη μείωση και άλλες επαναχρησιμοποιήσιμες λειτουργίες πρώτης κατηγορίας μέσω του API ροών.

Θα εξετάσουμε αυτές τις βελτιώσεις μαζί στις επόμενες ενότητες.

Γράφοντας λάμδα σε κώδικα Java

ΕΝΑ λάμδα είναι μια έκφραση που περιγράφει μια συνάρτηση υποδηλώνοντας την εφαρμογή μιας λειτουργικής διεπαφής. Ακολουθεί ένα παράδειγμα:

() -> System.out.println ("το πρώτο μου λάμδα")

Απο αριστερά προς δεξιά, () προσδιορίζει τη λίστα τυπικών παραμέτρων του λάμδα (δεν υπάρχουν παράμετροι), -> σημαίνει έκφραση λάμδα, και System.out.println ("το πρώτο μου λάμδα") είναι το σώμα του λάμδα (ο κωδικός που πρέπει να εκτελεστεί).

Ένα λάμδα έχει ένα τύπος, η οποία είναι οποιαδήποτε λειτουργική διεπαφή για την οποία το lambda είναι μια εφαρμογή. Ένας τέτοιος τύπος είναι java.lang. Εκτελέσιμο, επειδή Τρέξιμο'μικρό άκυρη εκτέλεση () Η μέθοδος έχει επίσης μια κενή λίστα τυπικών παραμέτρων:

Runnable r = () -> System.out.println ("το πρώτο μου λάμδα");

Μπορείτε να περάσετε το λάμδα οπουδήποτε από αυτό Τρέξιμο απαιτείται επιχείρημα. για παράδειγμα, το Νήμα (Runnable r) κατασκευαστής. Υποθέτοντας ότι η προηγούμενη εργασία έχει συμβεί, θα μπορούσατε να περάσετε ρ σε αυτόν τον κατασκευαστή, ως εξής:

νέο νήμα (r);

Εναλλακτικά, μπορείτε να περάσετε το λάμδα απευθείας στον κατασκευαστή:

νέο νήμα (() -> System.out.println ("το πρώτο μου λάμδα"));

Αυτό είναι σίγουρα πιο συμπαγές από την προ-Java 8 έκδοση:

νέο νήμα (νέο Runnable () {@Override public void run () {System.out.println ("my first lambda");}});

Ένα φίλτρο αρχείων που βασίζεται σε λάμδα

Η προηγούμενη επίδειξη συναρτήσεων υψηλότερης τάξης παρουσίασε ένα φίλτρο αρχείων βασισμένο σε μια ανώνυμη εσωτερική κλάση. Εδώ είναι το ισοδύναμο με βάση το λάμδα:

Αρχείο [] txtFiles = νέο αρχείο ("."). ListFiles (p -> p.getAbsolutePath (). Berakhir με ("txt"));

Επιστρέψτε τις δηλώσεις σε εκφράσεις λάμδα

Στο Μέρος 1, ανέφερα ότι οι λειτουργικές γλώσσες προγραμματισμού λειτουργούν με εκφράσεις σε αντίθεση με τις δηλώσεις. Πριν από την Java 8, θα μπορούσατε σε μεγάλο βαθμό να εξαλείψετε τις δηλώσεις σε λειτουργικό προγραμματισμό, αλλά δεν θα μπορούσατε να εξαλείψετε το ΕΠΙΣΤΡΟΦΗ δήλωση.

Το παραπάνω κομμάτι κώδικα δείχνει ότι ένα λάμδα δεν απαιτεί α ΕΠΙΣΤΡΟΦΗ δήλωση για να επιστρέψετε μια τιμή (μια τιμή Boolean true / false, σε αυτήν την περίπτωση): απλώς καθορίζετε την έκφραση χωρίς ΕΠΙΣΤΡΟΦΗ [και προσθέστε] ένα ερωτηματικό. Ωστόσο, για τα lambda πολλαπλών δηλώσεων, θα χρειαστείτε ακόμα ΕΠΙΣΤΡΟΦΗ δήλωση. Σε αυτές τις περιπτώσεις πρέπει να τοποθετήσετε το σώμα του λάμδα ανάμεσα σε τιράντες ως εξής (μην ξεχάσετε το ερωτηματικό για να τερματίσετε τη δήλωση):

Αρχείο [] txtFiles = νέο αρχείο ("."). ListFiles (p -> {return p.getAbsolutePath (). BerakhirWith ("txt");});

Lambdas με λειτουργικές διεπαφές

Έχω δύο ακόμη παραδείγματα για να δείξω τη συνοχή των λάμδα. Κατ 'αρχάς, ας επανεξετάσουμε το κύριος() μέθοδο από το Είδος εφαρμογή που εμφανίζεται στην καταχώριση 2:

public static void main (String [] args) {String [] innerplanets = {"Mercury", "Venus", "Earth", "Mars"}; χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, (e1, e2) -> e1.compareTo (e2)); χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, (e1, e2) -> e2.compareTo (e1)); χωματερή (εσωτερικά πλανήτες) }

Μπορούμε επίσης να ενημερώσουμε το υπολ. () μέθοδο από το CurriedCalc εφαρμογή που εμφανίζεται στην καταχώριση 6:

στατική λειτουργία> calc (ακέραιος α) {return b -> c -> d -> (a + b) * (c + d); }

Τρέξιμο, Φίλτρο αρχείων, και Συγκριτής είναι παραδείγματα λειτουργικές διεπαφές, που περιγράφουν συναρτήσεις. Η Java 8 επισημοποίησε αυτήν την ιδέα απαιτώντας μια λειτουργική διεπαφή να σχολιάζεται με το java.lang.FunctionalInterface τύπος σχολιασμού, όπως στο @FunctionalInterface. Μια διεπαφή που σχολιάζεται με αυτόν τον τύπο πρέπει να δηλώνει ακριβώς μια αφηρημένη μέθοδο.

Μπορείτε να χρησιμοποιήσετε τις προκαθορισμένες λειτουργικές διεπαφές της Java (θα συζητηθούν αργότερα) ή μπορείτε εύκολα να καθορίσετε τις δικές σας, ως εξής:

@FunctionalInterface interface Function {R apply (T t); }

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

public static void main (String [] args) {System.out.println (getValue (t -> (int) (Math.random () * t), 10)); System.out.println (getValue (x -> x * x, 20)); } στατικό Integer getValue (Function f, int x) {return f.apply (x); }

Είστε νέοι στο lambdas;

Εάν είστε νέοι στο lambdas, ίσως χρειαστείτε περισσότερο υπόβαθρο για να κατανοήσετε αυτά τα παραδείγματα. Σε αυτήν την περίπτωση, ρίξτε μια ματιά στην περαιτέρω εισαγωγή μου στα lambda και τις λειτουργικές διεπαφές στο "Ξεκινήστε με τις εκφράσεις lambda στην Java." Θα βρείτε επίσης πολλές χρήσιμες αναρτήσεις ιστολογίου σε αυτό το θέμα. Ένα παράδειγμα είναι ο "Λειτουργικός προγραμματισμός με λειτουργίες Java 8", στον οποίο ο συγγραφέας Edwin Dalorzo δείχνει πώς να χρησιμοποιεί εκφράσεις λάμδα και ανώνυμες συναρτήσεις στο Java 8

Αρχιτεκτονική λάμδα

Κάθε λάμδα είναι τελικά ένα παράδειγμα κάποιας κλάσης που δημιουργείται πίσω από τα παρασκήνια. Εξερευνήστε τους παρακάτω πόρους για να μάθετε περισσότερα σχετικά με την αρχιτεκτονική lambda:

  • "Πώς λειτουργούν τα lambdas και τα ανώνυμα εσωτερικά μαθήματα" (Martin Farrell, DZone)
  • "Lambdas in Java: Μια ματιά κάτω από την κουκούλα" (Brian Goetz, GOTO)
  • "Γιατί χρησιμοποιείται το Java 8 lambdas με χρήση invokedynamic;" (Υπερχείλιση στοίβας)

Νομίζω ότι θα βρείτε την παρουσίαση βίντεο του Αρχιτέκτονα Γλώσσας Java Brian Goetz για το τι συμβαίνει κάτω από την κουκούλα με λάμδα ιδιαίτερα συναρπαστικό.

Αναφορές μεθόδου σε Java

Μερικά λάμδα επικαλούνται μόνο μια υπάρχουσα μέθοδο. Για παράδειγμα, επικαλείται το ακόλουθο λάμδα System.out'μικρό άκυρη εκτύπωση μέθοδος στο μοναδικό επιχείρημα του lambda:

(String s) -> System.out.println (s)

Το λάμδα παρουσιάζει (Συμβολοσειρά) ως επίσημη λίστα παραμέτρων και ένα σώμα κώδικα του οποίου System.out.println (s) εκτυπώσεις έκφρασης μικρότιμή για την τυπική ροή εξόδου.

Για να αποθηκεύσετε πατήματα πλήκτρων, μπορείτε να αντικαταστήσετε το λάμδα με ένα αναφορά μεθόδου, που είναι μια συμπαγής αναφορά σε μια υπάρχουσα μέθοδο. Για παράδειγμα, θα μπορούσατε να αντικαταστήσετε το προηγούμενο τμήμα κώδικα με τα εξής:

System.out :: println

Εδώ, :: σημαίνει ότι System.out'μικρό void println (συμβολοσειρά) αναφέρεται η μέθοδος. Η αναφορά μεθόδου έχει ως αποτέλεσμα πολύ μικρότερο κώδικα από αυτόν που επιτύχαμε με το προηγούμενο λάμδα.

Μια αναφορά μεθόδου για Ταξινόμηση

Προηγουμένως έδειξα μια έκδοση λάμδα του Είδος εφαρμογή από την καταχώριση 2. Εδώ είναι ο ίδιος κωδικός γραμμένος με μια μέθοδο αναφοράς αντί:

public static void main (String [] args) {String [] innerplanets = {"Mercury", "Venus", "Earth", "Mars"}; χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, String :: membandingkanTo); χωματερή (εσωτερικά πλανήτες) ταξινόμηση (innerplanets, Comparator.comparing (String :: toString) .reversed ()); χωματερή (εσωτερικά πλανήτες) }

ο Συμβολοσειρά :: membandingkanTo Η έκδοση αναφοράς μεθόδου είναι μικρότερη από την έκδοση lambda του (e1, e2) -> e1.compareTo (e2). Σημειώστε, ωστόσο, ότι απαιτείται μεγαλύτερη έκφραση για να δημιουργήσετε ένα ισοδύναμο είδος αντίστροφης σειράς, το οποίο περιλαμβάνει επίσης μια αναφορά μεθόδου: Συμβολοσειρά :: toString. Αντί να καθορίσετε Συμβολοσειρά :: toString, Θα μπορούσα να καθορίσω το αντίστοιχο s -> s.toString () λάμδα.

Περισσότερα σχετικά με τις αναφορές μεθόδων

Υπάρχουν πολύ περισσότερα για αναφορές μεθόδων από ό, τι θα μπορούσα να καλύψω σε περιορισμένο χώρο. Για να μάθετε περισσότερα, ανατρέξτε στην εισαγωγή μου σχετικά με τη συγγραφή αναφορών μεθόδων για στατικές μεθόδους, μη στατικές μεθόδους και κατασκευαστές στο "Ξεκινήστε με αναφορές μεθόδων στην Java".

Προκαθορισμένες λειτουργικές διεπαφές

Η Java 8 παρουσίασε προκαθορισμένες λειτουργικές διεπαφές (java.util.function) έτσι ώστε οι προγραμματιστές να μην έχουν δημιουργήσει τις δικές μας λειτουργικές διεπαφές για κοινές εργασίες. Ακολουθούν μερικά παραδείγματα:

  • ο Καταναλωτής Η λειτουργική διεπαφή αντιπροσωπεύει μια λειτουργία που δέχεται ένα όρισμα εισόδου και δεν επιστρέφει κανένα αποτέλεσμα. Του άκυρη αποδοχή (T t) Η μέθοδος εκτελεί αυτήν τη λειτουργία στο όρισμα τ.
  • ο Λειτουργία η λειτουργική διεπαφή αντιπροσωπεύει μια συνάρτηση που αποδέχεται ένα όρισμα και επιστρέφει ένα αποτέλεσμα. Του R ισχύουν (T t) μέθοδος εφαρμόζει αυτήν τη συνάρτηση στο όρισμα τ και επιστρέφει το αποτέλεσμα.
  • ο Κατηγορούμενο η λειτουργική διεπαφή αντιπροσωπεύει ένα κατηγορούμενο (Boolean-valued function) ενός ορίσματος. Του δοκιμή boolean (T t) Η μέθοδος αξιολογεί αυτό το κατηγορηματικό επιχείρημα τ και επιστρέφει αληθές ή ψευδές.
  • ο Προμηθευτής η λειτουργική διεπαφή αντιπροσωπεύει έναν προμηθευτή αποτελεσμάτων. Του Λήψη () Η μέθοδος δεν λαμβάνει κανένα όρισμα, αλλά επιστρέφει ένα αποτέλεσμα.

ο DaysInMonth Η εφαρμογή στην Λίστα 1 αποκάλυψε ένα πλήρες Λειτουργία διεπαφή. Ξεκινώντας με το Java 8, μπορείτε να καταργήσετε αυτήν τη διεπαφή και να εισαγάγετε το ίδιο προκαθορισμένο Λειτουργία διεπαφή.

Περισσότερα για τις προκαθορισμένες λειτουργικές διεπαφές

"Ξεκινήστε με εκφράσεις λάμδα στην Java" παρέχει παραδείγματα του Καταναλωτής και Κατηγορούμενο λειτουργικές διεπαφές. Ρίξτε μια ματιά στην ανάρτηση ιστολογίου "Java 8 - Lazy επιχειρήματα αξιολόγησης" για να ανακαλύψετε μια ενδιαφέρουσα χρήση για Προμηθευτής.

Επιπλέον, ενώ οι προκαθορισμένες λειτουργικές διεπαφές είναι χρήσιμες, παρουσιάζουν επίσης ορισμένα ζητήματα. Ο Blogger Pierre-Yves Saumont εξηγεί γιατί.

Λειτουργικά API: Ροές

Η Java 8 παρουσίασε το API ροών για να διευκολύνει τη διαδοχική και παράλληλη επεξεργασία των στοιχείων δεδομένων. Αυτό το API βασίζεται ροές, όπου ένας ρεύμα είναι μια ακολουθία στοιχείων που προέρχονται από μια πηγή και υποστηρίζουν διαδοχικές και παράλληλες συγκεντρωτικές λειτουργίες. ΕΝΑ πηγή αποθηκεύει στοιχεία (όπως μια συλλογή) ή δημιουργεί στοιχεία (όπως μια γεννήτρια τυχαίων αριθμών). Ενα σύνολο είναι ένα αποτέλεσμα που υπολογίζεται από πολλαπλές τιμές εισόδου.

Μια ροή υποστηρίζει ενδιάμεσες και τερματικές εργασίες. Ενα ενδιάμεση λειτουργία επιστρέφει μια νέα ροή, ενώ a τερματική λειτουργία καταναλώνει τη ροή. Οι λειτουργίες συνδέονται σε ένα αγωγός (μέσω αλυσίδας μεθόδου). Ο αγωγός ξεκινά με μια πηγή, η οποία ακολουθείται από μηδενικές ή περισσότερες ενδιάμεσες λειτουργίες και τελειώνει με μια τερματική λειτουργία.

Οι ροές είναι ένα παράδειγμα ενός λειτουργικό API. Προσφέρει φίλτρα, χάρτη, μείωση και άλλες επαναχρησιμοποιήσιμες λειτουργίες πρώτης κατηγορίας. Έδειξα εν συντομία αυτό το API στο Υπαλλήλους εφαρμογή που εμφανίζεται στο Μέρος 1, Λίστα 1. Η λίστα 7 προσφέρει ένα άλλο παράδειγμα.

Λίστα 7. Λειτουργικός προγραμματισμός με ροές (StreamFP.java)

εισαγωγή java.util.Random; εισαγωγή java.util.stream.IntStream; δημόσια τάξη StreamFP {public static void main (String [] args) {new Random (). ints (0, 11) .limit (10) .filter (x -> x% 2 == 0) .forEach (System.out :: println); System.out.println (); String [] city = {"New York", "London", "Paris", "Berlin", "BrasÌlia", "Tokyo", "Beijing", "Jerusalem", "Cairo", "Riyadh", "Moscow" }; IntStream.range (0, 11) .mapToObj (i -> πόλεις [i]) .forEach (System.out :: println); System.out.println (); System.out.println (IntStream.range (0, 10). Μείωση (0, (x, y) -> x + y)); System.out.println (IntStream.range (0, 10). Reduce (0, Integer :: sum)); }}

ο κύριος() Η μέθοδος δημιουργεί πρώτα μια ροή ψευδοτυχαίων ακεραίων που ξεκινά από το 0 και τελειώνει στο 10. Η ροή περιορίζεται σε ακριβώς 10 ακέραιους. ο φίλτρο() Η συνάρτηση πρώτης κατηγορίας λαμβάνει ένα λάμδα ως βασικό επιχείρημα. Το κατηγορηματικό αφαιρεί περίεργους ακέραιους αριθμούς από τη ροή. Τέλος, το για κάθε() Η λειτουργία πρώτης κατηγορίας εκτυπώνει κάθε ομοιόμορφο ακέραιο στην τυπική έξοδο μέσω του System.out :: println αναφορά μεθόδου.

ο κύριος() Στη συνέχεια, η μέθοδος δημιουργεί μια ακέραια ροή που παράγει ένα διαδοχικό εύρος ακεραίων ξεκινώντας από το 0 και τελειώνει στο 10. Το mapToObj () Η συνάρτηση πρώτης κατηγορίας λαμβάνει ένα λάμδα που αντιστοιχίζει έναν ακέραιο με την ισοδύναμη συμβολοσειρά στο ευρετήριο ακέραιου στο πόλεις πίνακας. Στη συνέχεια, το όνομα της πόλης αποστέλλεται στην τυπική έξοδο μέσω του για κάθε() λειτουργία πρώτης κατηγορίας και της System.out :: println αναφορά μεθόδου.

Εν τέλει, κύριος() καταδεικνύει το περιορίζω() λειτουργία πρώτης κατηγορίας. Μια ακέραια ροή που παράγει το ίδιο εύρος ακεραίων με το προηγούμενο παράδειγμα μειώνεται σε ένα άθροισμα των τιμών τους, το οποίο στη συνέχεια εξάγεται.

Προσδιορισμός των ενδιάμεσων και τερματικών πράξεων

Καθένα από όριο(), φίλτρο(), εύρος(), και mapToObj () είναι ενδιάμεσες πράξεις, ενώ για κάθε() και περιορίζω() είναι τερματικές εργασίες.

Συγκεντρώστε την καταχώριση 7 ως εξής:

javac StreamFP.java

Εκτελέστε την εφαρμογή που προκύπτει ως εξής:

java StreamFP

Παρατήρησα την ακόλουθη έξοδο από μία εκτέλεση:

0 2 10 6 0 8 10 Νέα Υόρκη Λονδίνο Παρίσι Βερολίνο BrasÌlia Τόκιο Πεκίνο Ιερουσαλήμ Κάιρο Ριάντ Μόσχα 45 45

Ίσως περιμένατε 10 αντί για 7 ψευδοτυχαίους ακέραιους ακέραιους αριθμούς (που κυμαίνονται από 0 έως 10, χάρη στο εύρος (0, 11)) για να εμφανιστεί στην αρχή της εξόδου. Παρά όλα αυτά, όριο (10) φαίνεται να δείχνουν ότι 10 ακέραιοι αριθμοί θα εξάγονται. Ωστόσο, αυτό δεν ισχύει. παρόλο που το όριο (10) αποτελέσματα κλήσεων σε μια ροή ακριβώς 10 ακέραιων αριθμών, το φίλτρο (x -> x% 2 == 0) Τα αποτελέσματα των κλήσεων καταργούν περίεργους ακέραιους αριθμούς από τη ροή.

Περισσότερα για τις Ροές

Εάν δεν είστε εξοικειωμένοι με τις ροές, ρίξτε μια ματιά στο σεμινάριό μου που παρουσιάζει το νέο API ροών του Java SE 8 για περισσότερες πληροφορίες σχετικά με αυτό το λειτουργικό API.

Συμπερασματικά

Πολλοί προγραμματιστές Java δεν θα ακολουθήσουν καθαρό λειτουργικό προγραμματισμό σε μια γλώσσα όπως το Haskell, επειδή διαφέρει τόσο πολύ από το οικείο επιτακτικό, αντικειμενοστρεφές παράδειγμα. Οι λειτουργικές δυνατότητες προγραμματισμού του Java 8 έχουν σχεδιαστεί για να γεφυρώσουν αυτό το κενό, επιτρέποντας στους προγραμματιστές της Java να γράψουν κώδικα που είναι ευκολότερο να κατανοηθεί, να διατηρηθεί και να δοκιμαστεί. Ο λειτουργικός κώδικας είναι επίσης πιο επαναχρησιμοποιήσιμος και πιο κατάλληλος για παράλληλη επεξεργασία στην Java. Με όλα αυτά τα κίνητρα, δεν υπάρχει κανένας λόγος να μην ενσωματώσετε τις λειτουργικές επιλογές προγραμματισμού της Java στον κώδικα Java.

Γράψτε μια λειτουργική εφαρμογή Bubble Sort

Λειτουργική σκέψη είναι ένας όρος που επινοήθηκε από τον Neal Ford, ο οποίος αναφέρεται στη γνωστική μετατόπιση από το αντικειμενοστρεφό παράδειγμα στο λειτουργικό πρότυπο προγραμματισμού. Όπως έχετε δει σε αυτό το σεμινάριο, είναι δυνατό να μάθετε πολλά για τον λειτουργικό προγραμματισμό ξαναγράφοντας αντικειμενοστρεφή κώδικα χρησιμοποιώντας λειτουργικές τεχνικές.

Περάστε ό, τι έχετε μάθει μέχρι στιγμής επανεξετάζοντας την εφαρμογή Ταξινόμηση από την Καταχώριση 2. Σε αυτήν τη γρήγορη συμβουλή, θα σας δείξω πώς να το κάνετε γράψτε ένα καθαρά λειτουργικό Bubble Sort, πρώτα χρησιμοποιώντας τεχνικές προ-Java 8 και, στη συνέχεια, χρησιμοποιήστε τις λειτουργικές δυνατότητες του Java 8.

Αυτή η ιστορία, "Λειτουργικός προγραμματισμός για προγραμματιστές Java, Μέρος 2" δημοσιεύθηκε αρχικά από την JavaWorld.