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

Προκλητικό δυναμικό 101

Η έκδοση Java 7 της Oracle παρουσίασε ένα νέο επικαλυμμένο δυναμικό εντολή bytecode στην εικονική μηχανή Java (JVM) και μια νέα java.lang.invoke Πακέτο API στη βασική βιβλιοθήκη τάξης. Αυτή η ανάρτηση σάς παρουσιάζει αυτήν την οδηγία και το API.

Τι και πώς γίνεται το invokedynamic

Ε: Τι είναι επικαλυμμένο δυναμικό?

ΕΝΑ:επικαλυμμένο δυναμικό είναι μια εντολή bytecode που διευκολύνει την εφαρμογή δυναμικών γλωσσών (για το JVM) μέσω της δυναμικής επίκλησης μεθόδου. Αυτή η οδηγία περιγράφεται στην έκδοση Java SE 7 της προδιαγραφής JVM.

Δυναμικές και στατικές γλώσσες

ΕΝΑ δυναμική γλώσσα (επίσης γνωστό ως δυναμικά δακτυλογραφημένη γλώσσα) είναι μια γλώσσα προγραμματισμού υψηλού επιπέδου της οποίας ο έλεγχος τύπου πραγματοποιείται συνήθως κατά το χρόνο εκτέλεσης, μια γνωστή λειτουργία ως δυναμική πληκτρολόγηση. Ο έλεγχος τύπου επιβεβαιώνει ότι είναι ένα πρόγραμμα πληκτρολογήστε ασφαλή: όλα τα ορίσματα λειτουργίας έχουν τον σωστό τύπο. Οι Groovy, Ruby και JavaScript είναι παραδείγματα δυναμικών γλωσσών. (Ο @ groovy.transform.TypeChecked Ο σχολιασμός προκαλεί τον Groovy να πληκτρολογήσει τον έλεγχο κατά τη στιγμή της μεταγλώττισης.)

Αντίθετα, α στατική γλώσσα (επίσης γνωστό ως στατικά δακτυλογραφημένη γλώσσα) εκτελεί έλεγχο τύπου κατά τη στιγμή της μεταγλώττισης, ένα χαρακτηριστικό γνωστό ως στατική πληκτρολόγηση. Ο μεταγλωττιστής επαληθεύει ότι ένα πρόγραμμα είναι σωστού τύπου, αν και μπορεί να αναβάλει τον έλεγχο κάποιου τύπου στο χρόνο εκτέλεσης (think casts και το checkcast εντολή). Η Java είναι ένα παράδειγμα στατικής γλώσσας. Ο μεταγλωττιστής Java χρησιμοποιεί αυτές τις πληροφορίες τύπου για να παράγει έντονα δακτυλογραφημένο bytecode, ο οποίος μπορεί να εκτελεστεί αποτελεσματικά από το JVM.

Ε: Πώς κάνει επικαλυμμένο δυναμικό διευκολύνει τη δυναμική εφαρμογή της γλώσσας;

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

Μια άλλη πρόκληση είναι ότι οι δυναμικές γλώσσες προσφέρουν συνήθως τη δυνατότητα προσθήκης πεδίων / μεθόδων και την κατάργησή τους από υπάρχουσες τάξεις. Ως αποτέλεσμα, είναι απαραίτητο να αναβληθεί η ανάλυση τάξης, μεθόδου και πεδίου έως το χρόνο εκτέλεσης. Επίσης, είναι συχνά απαραίτητο να προσαρμόσετε μια επίκληση μεθόδου σε έναν στόχο που έχει διαφορετική υπογραφή.

Αυτές οι προκλήσεις απαιτούν παραδοσιακά την υποστήριξη ad hoc χρόνου εκτέλεσης πάνω από το JVM. Αυτή η υποστήριξη περιλαμβάνει τάξεις τύπου περιτυλίγματος, χρησιμοποιώντας πίνακες κατακερματισμού για την παροχή δυναμικής ανάλυσης συμβόλων και ούτω καθεξής. Το Bytecode δημιουργείται με σημεία εισόδου στο χρόνο εκτέλεσης με τη μορφή μεθόδων κλήσεων χρησιμοποιώντας οποιαδήποτε από τις τέσσερις οδηγίες μεθόδου-επίκλησης:

  • invokestatic χρησιμοποιείται για επίκληση στατικός μεθόδους.
  • invokevirtual χρησιμοποιείται για επίκληση δημόσιο και προστατευμένο μη-στατικός μεθόδους μέσω δυναμικής αποστολής.
  • invokeinterface είναι παρόμοιο με invokevirtual εκτός από τη μέθοδο αποστολής που βασίζεται σε έναν τύπο διασύνδεσης.
  • αδιάφορο χρησιμοποιείται για την επίκληση μεθόδων αρχικοποίησης παρουσίας (κατασκευαστές) επίσης ιδιωτικός μέθοδοι και μέθοδοι ενός superclass της τρέχουσας κατηγορίας.

Αυτή η υποστήριξη χρόνου εκτέλεσης επηρεάζει την απόδοση. Το bytecode που δημιουργείται συχνά απαιτεί αρκετές πραγματικές κλήσεις μεθόδου JVM για μια επίκληση μιας δυναμικής μεθόδου γλώσσας. Ο προβληματισμός χρησιμοποιείται ευρέως και συμβάλλει στην υποβάθμιση της απόδοσης. Επίσης, οι πολλές διαφορετικές διαδρομές εκτέλεσης καθιστούν αδύνατο για τον μεταγλωττιστή JVM just-in-time (JIT) να εφαρμόζει βελτιστοποιήσεις.

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

επικαλυμμένο δυναμικό ωφελεί επίσης τους δυναμικούς εφαρμοστές γλώσσας υποστηρίζοντας δυναμικά μεταβαλλόμενους στόχους ιστότοπων κλήσεων - ιστότοπος κλήσεων, πιο συγκεκριμένα, α δυναμικός ιστότοπος κλήσεων είναι ένα επικαλυμμένο δυναμικό εντολή. Επιπλέον, επειδή το JVM υποστηρίζει εσωτερικά επικαλυμμένο δυναμικό, αυτή η οδηγία μπορεί να βελτιστοποιηθεί καλύτερα από τον μεταγλωττιστή JIT.

Λαβές μεθόδου

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

ΕΝΑ: ΕΝΑ λαβή μεθόδου είναι "μια δακτυλογραφημένη, άμεσα εκτελέσιμη αναφορά σε μια υποκείμενη μέθοδο, κατασκευαστή, πεδίο ή παρόμοια λειτουργία χαμηλού επιπέδου, με προαιρετικούς μετασχηματισμούς ορισμών ή τιμών επιστροφής." Με άλλα λόγια, είναι παρόμοιο με ένα δείκτη συνάρτησης τύπου C που δείχνει τον εκτελέσιμο κώδικα - α στόχος - και το οποίο μπορεί να ανακληθεί για να επικαλεστεί αυτόν τον κωδικό. Οι λαβές της μεθόδου περιγράφονται από την περίληψη java.lang.invoke.MethodHandle τάξη.

Ε: Μπορείτε να δώσετε ένα απλό παράδειγμα δημιουργίας και επίκλησης χειρισμού μεθόδου;

ΕΝΑ: Δείτε την Καταχώριση 1.

Λίστα 1. MHD.java (έκδοση 1)

εισαγωγή java.lang.invoke.MethodHandle; εισαγωγή java.lang.invoke.MethodHandles; εισαγωγή java.lang.invoke.MethodType; δημόσια τάξη MHD {public static void main (String [] args) ρίχνει Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findStatic (MHD.class, "γεια", MethodType.methodType (void.class)); mh.invokeExact (); } static void hello () {System.out.println ("γεια"); }}

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

κύριος()Το πρώτο καθήκον είναι να αποκτήσετε ένα java.lang.invoke.MethodHandles.Lookup αντικείμενο. Αυτό το αντικείμενο είναι ένα εργοστάσιο για τη δημιουργία λαβών μεθόδων και χρησιμοποιείται για την αναζήτηση στόχων όπως εικονικές μέθοδοι, στατικές μέθοδοι, ειδικές μέθοδοι, κατασκευαστές και βοηθητικά πεδία. Επιπλέον, εξαρτάται από το πλαίσιο επίκλησης ενός ιστότοπου κλήσεων και επιβάλλει περιορισμούς πρόσβασης στη διαχείριση μεθόδου κάθε φορά που δημιουργείται μια λαβή μεθόδου. Με άλλα λόγια, ένας ιστότοπος κλήσεων (όπως η Λίστα 1) κύριος() μέθοδος που λειτουργεί ως ιστότοπος κλήσεων) που λαμβάνει ένα αντικείμενο αναζήτησης μπορεί να έχει πρόσβαση μόνο στους στόχους που είναι προσβάσιμοι στον ιστότοπο κλήσεων. Το αντικείμενο αναζήτησης επιτυγχάνεται με την επίκληση του java.lang.invoke.MethodHandles της τάξης MethodHandles. Αναζήτηση αναζήτησης () μέθοδος.

δημόσια αναζήτηση ()

Μέθοδος χειρισμών δηλώνει επίσης α MethodHandles.Lookup publicLookup () μέθοδος. Διαφορετικός ψάχνω(), η οποία μπορεί να χρησιμοποιηθεί για τη λήψη λαβής μεθόδου σε οποιαδήποτε προσβάσιμη μέθοδο / κατασκευαστή ή πεδίο, δημόσια αναζήτηση () μπορεί να χρησιμοποιηθεί για να αποκτήσει μια λαβή μεθόδου σε ένα προσβάσιμο από το κοινό πεδίο ή μόνο για δημόσια προσβάσιμη μέθοδο / κατασκευαστή.

Αφού αποκτήσετε το αντικείμενο αναζήτησης, αυτό το αντικείμενο MethodHandle findStatic (Αναφορά κλάσης, όνομα συμβολοσειράς, τύπος μεθόδου τύπου) η μέθοδος καλείται να αποκτήσει μια λαβή μεθόδου στο γεια() μέθοδος. Το πρώτο επιχείρημα πέρασε εύρεσηStatic () είναι μια αναφορά στην τάξη (MHD) από την οποία η μέθοδος (γεια()) έχει πρόσβαση και το δεύτερο όρισμα είναι το όνομα της μεθόδου. Το τρίτο επιχείρημα είναι ένα παράδειγμα του a τύπος μεθόδου, το οποίο "αντιπροσωπεύει τα ορίσματα και τον τύπο επιστροφής αποδεκτό και επιστρέφεται από μια λαβή μεθόδου, ή τα ορίσματα και τον τύπο επιστροφής πέρασε και αναμένεται από έναν καλούντα χειρισμού μεθόδου." Αντιπροσωπεύεται από μια παρουσία του java.lang.invoke.MethodType τάξη και αποκτήθηκε (σε αυτό το παράδειγμα) καλώντας java.lang.invoke.MethodType'μικρό MethodType methodType (Κατηγορία rtype) μέθοδος. Αυτή η μέθοδος ονομάζεται επειδή γεια() παρέχει μόνο έναν τύπο επιστροφής, ο οποίος τυχαίνει να είναι κενός. Αυτός ο τύπος επιστροφής διατίθεται στο τύπος μεθόδου () περνώντας void.class σε αυτήν τη μέθοδο.

Η λαβή μεθόδου που επιστράφηκε αντιστοιχίστηκε σε ωμ. Αυτό το αντικείμενο χρησιμοποιείται στη συνέχεια για κλήση Μέθοδος χειρισμού'μικρό Object invokeExact (Object ... args) μέθοδο, για να επικαλεστεί τη λαβή μεθόδου. Με άλλα λόγια, invokeExact () αποτελέσματα σε γεια() καλείται, και γεια γράφεται στην τυπική ροή εξόδου. Επειδή invokeExact () δηλώνεται ότι ρίχνει Ρίξιμο, Έχω επισυνάψει ρίχνει Throwable στο κύριος() κεφαλίδα μεθόδου.

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

ΕΝΑ: Δείτε την καταχώριση 2.

Λίστα 2. MHD.java (έκδοση 2)

εισαγωγή java.lang.invoke.MethodHandle; εισαγωγή java.lang.invoke.MethodHandles; εισαγωγή java.lang.invoke.MethodType; τάξη HW {public void hello1 () {System.out.println ("hello from hello1"); } ιδιωτικό κενό hello2 () {System.out.println ("hello from hello2"); }} δημόσια τάξη MHD {public static void main (String [] args) ρίχνει Throwable {HW hw = new HW (); MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findVirtual (HW.class, "hello1", MethodType.methodType (void.class)); mh.invoke (hw); mh = lookup.findVirtual (HW.class, "hello2", MethodType.methodType (void.class)); }}

Η λίστα 2 δηλώνει HW (Γεια, Κόσμος) και MHD τάξεις. HW δηλώνει α δημόσιογεια1 () μέθοδος παρουσίας και α ιδιωτικόςγεια 2 () μέθοδος παρουσίας. MHD δηλώνει α κύριος() μέθοδος που θα προσπαθήσει να επικαλεστεί αυτές τις μεθόδους.

κύριος()Το πρώτο καθήκον είναι να δημιουργηθεί HW σε προετοιμασία για επίκληση γεια1 () και γεια 2 (). Στη συνέχεια, λαμβάνει ένα αντικείμενο αναζήτησης και χρησιμοποιεί αυτό το αντικείμενο για να αποκτήσει μια λαβή μεθόδου για επίκληση γεια1 (). Αυτή τη φορά, MethodHandles.Lookup'μικρό βρείτεVirtual () καλείται μέθοδος και το πρώτο όρισμα που μεταδίδεται σε αυτήν τη μέθοδο είναι α Τάξη αντικείμενο που περιγράφει το HW τάξη.

Τελικά φαίνεται πως βρείτεVirtual () θα πετύχει, και το επόμενο mh.invoke (hw); η έκφραση θα επικαλεστεί γεια1 (), έχοντας ως αποτέλεσμα γεια από γειά σου1 είναι έξοδος.

Επειδή γεια1 () είναι δημόσιο, είναι προσβάσιμο στο κύριος() μέθοδος κλήσης ιστότοπου. Σε αντίθεση, γεια 2 () δεν είναι προσβάσιμο. Ως αποτέλεσμα, το δεύτερο βρείτεVirtual () η επίκληση θα αποτύχει με ένα IlegalAccessException.

Όταν εκτελείτε αυτήν την εφαρμογή, θα πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

hello from hello1 Εξαίρεση στο νήμα "main" java.lang.IllegalAccessException: το μέλος είναι ιδιωτικό: HW.hello2 () άκυρο, από το MHD στο java.lang.invoke.MemberName.makeAccessException (Όνομα μέλους.java:507) στο java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java[172) στο java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java : 152) στο java.lang.invoke.MethodHandles $ Lookup.accessVirtual (MethodHandles.java: 648) στο java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) στο MHD.main (MHD.java:27)

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

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

Ε: Μπορείτε να μου δώσετε ένα παράδειγμα που δείχνει πώς να επικαλεστώ τη λήψη και τη ρύθμιση του πεδίου παρουσίας;

ΕΝΑ: Δείτε την καταχώριση 3.

Λίστα 3. MHD.java (έκδοση 3)

εισαγωγή java.lang.invoke.MethodHandle; εισαγωγή java.lang.invoke.MethodHandles; εισαγωγή java.lang.invoke.MethodType; Σημείο τάξης {int x; int y; } δημόσια τάξη MHD {public static void main (String [] args) ρίχνει Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Σημείο σημείου = νέο σημείο (); // Ορίστε τα πεδία x και y. MethodHandle mh = lookup.findSetter (Point.class, "x", int.class); mh.invoke (σημείο 15); mh = lookup.findSetter (Point.class, "y", int.class); mh.invoke (σημείο 30); mh = lookup.findGetter (Point.class, "x", int.class); int x = (int) mh. invoke (σημείο); System.out.printf ("x =% d% n", x); mh = lookup.findGetter (Point.class, "y", int.class); int y = (int) mh.invoke (σημείο); System.out.printf ("y =% d% n", y); }}

Η λίστα 3 εισάγει ένα Σημείο κλάση με ένα ζεύγος ακέραιων πεδίων παρουσίας 32-bit με όνομα Χ και γ. Ο ρυθμιστής και ο παίκτης κάθε πεδίου έχουν πρόσβαση μέσω κλήσης MethodHandles.Lookup'μικρό εύρεσηSetter () και βρείτεGetter () μεθόδους και τα προκύπτοντα Μέθοδος χειρισμού επιστρέφεται. Καθένα από εύρεσηSetter () και βρείτεGetter () απαιτεί ένα Τάξη όρισμα που προσδιορίζει την κλάση του πεδίου, το όνομα του πεδίου και ένα Τάξη αντικείμενο που προσδιορίζει την υπογραφή του πεδίου.

ο επικαλούμαι() η μέθοδος χρησιμοποιείται για την εκτέλεση ενός ρυθμιστή ή λήψης - πίσω από τα παρασκήνια, τα πεδία παρουσίας είναι προσβάσιμα μέσω των JVM putfield και getfield οδηγίες. Αυτή η μέθοδος απαιτεί μια αναφορά στο αντικείμενο του οποίου η πρόσβαση γίνεται να περάσει ως αρχικό όρισμα. Για επίκληση ρυθμιστών, πρέπει επίσης να περάσει ένα δεύτερο όρισμα, το οποίο αποτελείται από την τιμή που αποδίδεται στο πεδίο.

Όταν εκτελείτε αυτήν την εφαρμογή, θα πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

x = 15 y = 30

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

ΕΝΑ: Έχω δημιουργήσει ένα παράδειγμα βασισμένο στο Μαθηματικά της τάξης διπλή ισχύς (διπλή α, διπλή β) μέθοδος τάξης. Σε αυτό το παράδειγμα, λαμβάνω μια λαβή μεθόδου στο pow () μέθοδο, και μετατρέψτε αυτήν τη λαβή μεθόδου έτσι ώστε το δεύτερο όρισμα πέρασε pow () είναι πάντα 10. Δείτε την καταχώριση 4.