Κανόνες για τη χύτευση πρωτόγονων τύπων java. Τύπος Μετατροπή σε Java

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

Πώς λειτουργεί η χύτευση σαφούς τύπου

Το παράδειγμά σας δείχνει έναν ανοδικό μετασχηματισμό ( Upcasting):

List coll = new ArrayList();

Μεταφράζεται στα ρωσικά ως εξής: δημιουργήστε ένα κοράκι, σαν πουλί. Δημιουργήστε έναν δυναμικό πίνακα, όπως ένα φύλλο. Στις περισσότερες περιπτώσεις, μια ανοδική μετατροπή είναι εντελώς περιττή.
Ωστόσο, το casting τύπου λειτουργεί στις συνεντεύξεις όταν σας δίνονται ερωτήσεις κληρονομιάς. Για παράδειγμα, ο ιστότοπος quizful.net περιέχει γενικά πολλές ερωτήσεις σχετικά με τη χύτευση τύπου. Επομένως, θα εξηγήσω τα χαρακτηριστικά που γνωρίζω.

Έτσι, στο παραπάνω παράδειγμα, δημιουργήσαμε ένα αντικείμενο τύπου ArrayList και μια αναφορά τύπου List. Θυμηθείτε τα αξιώματα για αυτήν τη μέθοδο:

1. Ο σύνδεσμος μπορεί να υποδειχθεί σε οποιονδήποτε γονέα. Ακόμη και πολύ καιρό πριν. Δηλαδή, μπορείτε να ρίξετε μια αναφορά coll ακόμη και στον τύπο Object. Ο μεταγλωττιστής θα παραλείψει οποιαδήποτε αναφορά σε μια γονική κλάση, ή γονέα-γονέα ή γονέα-γονέα...γονέα

2. Κατά την πρόσβαση σε ένα πεδίο, επιστρέφεται πάντα το πεδίο αναφοράς, όχι το πεδίο αντικειμένου. Εάν δεν υπάρχει τέτοιο πεδίο στην κλάση αναφοράς, θα υπάρχει σφάλμα μεταγλώττισης.

Κλάση A( int x = 2; //Πεδίο γονέα) Η κλάση B επεκτείνει την A ( int x = 3; //Πεδίο που πρέπει να επικαλύπτει το γονικό int y = 5; //Πεδίο που δεν ανήκει στη γονική κλάση. ) Δοκιμή τάξης ( δημόσιο static void main (String args) ( A ab = new B(); //Upward conversion System.out.println("Int x = " + ab.x); ) )

Θα επιστρέψει Int x = 2 . Εάν προσπαθήσετε να αποκτήσετε πρόσβαση σε ένα πεδίο ενός αντικειμένου:

System.out.println("Int y = " + ab.y); //Σφάλμα μεταγλώττισης

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

3. Κλήση μη στατικής μεθόδου: σε αυτήν την περίπτωση, θα επιστραφεί η μέθοδος του αντικειμένου. Αλλά κατά την πρόσβαση σε μια στατική μέθοδο, επιστρέφει τη μέθοδο αναφοράς.

Κλάση D( public void doSome())( //Not στατική μέθοδος System.out.println("Μη στατικά doSome from D"); ) public static void Action())( //Static method System.out.println("static Action from D"); ) ) public class Okey extends D( public void doSome())( System.out.println("doSome από το Okey "); ) public static void Action())( System.out.println("static Action from Okey"); ) public static void main(String args) (D o=new Okey(); o.doSome( ); / /Από την κλάση Okey o.Action(); //Από την κλάση D ) )

Μη στατικό doSome από την Okey

στατική δράση από το Δ

Η λύση είναι απλή, μια μη στατική μέθοδος είναι μια μέθοδος αντικειμένου, μια στατική μέθοδος είναι μια μέθοδος κλάσης. Όταν καλούμε μια μη στατική μέθοδο, ο μεταγλωττιστής καταλαβαίνει αυτό: πετάξτε σαν κοράκι. Όταν λέμε στατικό - κυριολεκτικά, πετάξτε σαν πουλί.

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

Κλάση Α () Η κλάση Β επεκτείνει το A ( void someMethod())(); δημόσιο στατικό κενό main(String args) ( A ab = new B(); ab.someMethod(); //Σφάλμα μεταγλώττισης. ) )

5. Ο κατασκευαστής αντικειμένων (όταν δημιουργείται με τη νέα εντολή) λειτουργεί το ίδιο σαν να δίνατε έναν σύνδεσμο στην κλάση σας.

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

  • Πανομοιότυπο (ταυτότητα);

  • διεύρυνση πρωτόγονη?

  • Στένοντας πρωτόγονος;

  • Αναφορά διεύρυνσης τύπου αντικειμένου.

  • Περιορισμός αναφοράς;

  • Μετατροπή σε συμβολοσειρά.

  • Απαγορευμένοι μετασχηματισμοί (απαγορευμένοι);
Ας τα δούμε ξεχωριστά.
Μεταμόρφωση ταυτότητας
Το πιο απλό είναι ο μετασχηματισμός ταυτότητας. Στην Java, η μετατροπή μιας έκφρασης οποιουδήποτε τύπου στον ίδιο ακριβώς τύπο είναι πάντα έγκυρη και πετυχαίνει.
Αυτό είναι σημαντικό για να μπορούμε να δηλώσουμε από θεωρητική άποψη ότι οποιοσδήποτε τύπος στη Java μπορεί να συμμετέχει στη μετατροπή, τουλάχιστον σε μια μετατροπή ταυτότητας.
Μετατροπή πρωτόγονους τύπους(διαστολή και συστολή)
Για απλοί τύποιεπέκταση σημαίνει ότι υπάρχει μια μετάβαση από έναν λιγότερο χωρητικό τύπο σε έναν πιο ευρύχωρο. Για παράδειγμα, από τον τύπο byte (μήκος 1 byte) στον τύπο int (μήκος 4 byte). Τέτοιοι μετασχηματισμοί είναι ασφαλείς με την έννοια ότι νέου τύπουείναι πάντα εγγυημένο ότι περιέχει όλα τα δεδομένα που ήταν αποθηκευμένα στον παλιό τύπο και επομένως δεν υπάρχει απώλεια δεδομένων. Αυτός είναι ο λόγος για τον οποίο ο μεταγλωττιστής το υλοποιεί μόνος του, απαρατήρητος από τον προγραμματιστή:

byte b=3;
int a=b;

Οι παρακάτω 19 μετασχηματισμοί είναι επέκτασης:

  • Από byte σε short, int, long, float, double

  • Από short σε int, long, float, double

  • Από char σε int, long, float, double

  • Από int σε long, float, double

  • Από μακρύ σε float, διπλό

  • Από float σε διπλό
Λάβετε υπόψη ότι δεν μπορείτε να μετατρέψετε σε χαρακτήρες από τύπους μικρότερους από ή ίσου μήκους(byte, short) ή, αντίθετα, σε short από char χωρίς απώλεια δεδομένων. Αυτό οφείλεται στο γεγονός ότι το char, σε αντίθεση με άλλους ακέραιους τύπους, είναι υπογεγραμμένο.
Ωστόσο, θα πρέπει να θυμόμαστε ότι ακόμη και με την επέκταση, τα δεδομένα μπορούν να παραμορφωθούν. Αυτές είναι casting int τιμές σε float και casting long values ​​to float ή double. Αν και αυτοί οι κλασματικοί τύποι χωρούν πολλά μεγάλα νούμερα, από τους αντίστοιχους ακέραιους αριθμούς, αλλά έχουν λιγότερα σημαντικά ψηφία.
Για παράδειγμα:

μήκος a = 111111111111L;
float f=a;
a=(long)f; // () αυτή είναι ακριβώς η λειτουργία μετατροπής τύπου
System.out.println(a); //αποτέλεσμα 111111110656

Λάβετε υπόψη ότι το στένωση σημαίνει ότι η μετάβαση από έναν πιο ευρύχωρο τύπο σε έναν λιγότερο ευρύχωρο. Με αυτή τη μετατροπή υπάρχει κίνδυνος απώλειας δεδομένων. Για παράδειγμα, εάν ο αριθμός πληκτρολογήστε intήταν μεγαλύτερο από 127, τότε κατά τη μετατροπή του σε byte, οι τιμές των bit υψηλότερων από το όγδοο θα χαθούν. Στη Java αυτή η μετατροπή πρέπει να γίνει ρητά, δηλ. Ο προγραμματιστής πρέπει να αναφέρει ρητά στον κώδικα ότι σκοπεύει να πραγματοποιήσει μια τέτοια μετατροπή και ότι είναι έτοιμος να χάσει δεδομένα.
Οι ακόλουθοι 23 μετασχηματισμοί στενεύουν:

  • Από byte σε χαρακτήρες

  • Από σύντομο σε byte, χαρ

  • Από char σε byte, σύντομο

  • Από int σε byte, σύντομο, char

  • Από μακρύ σε byte, short, char, int

  • Από float σε byte, short, char, int, long

  • Από διπλό σε byte, short, char, int, long, float
Όταν στενεύει ακέραιος τύποςσε έναν στενότερο ακέραιο, όλα τα bit υψηλής τάξης που δεν εμπίπτουν στον νέο τύπο απλώς απορρίπτονται. Δεν εκτελούνται στρογγυλοποιήσεις ή άλλες ενέργειες για να επιτευχθεί πιο σωστό αποτέλεσμα:

System.out.println((byte)383);
System.out.println((byte)384);
System.out.println((byte)-384);

Το αποτέλεσμα θα είναι:

127
-128
-128
Μπορεί να φανεί ότι το κομμάτι της πινακίδας κατά το στένεμα δεν είχε κανένα αποτέλεσμα, αφού απλώς απορρίφθηκε - το αποτέλεσμα του καστ αμοιβαίοι αριθμοί(384, -384) αποδείχθηκε ότι ήταν το ίδιο. Κατά συνέπεια, μπορεί να χαθεί όχι μόνο η ακριβής απόλυτη τιμή, αλλά και το πρόσημο του μεγέθους.
Αυτό ισχύει και για τον χαρακτήρα:

char c=4000;
System.out.println((σύντομη)c);

Αποτέλεσμα:

-25536
Μετατροπή τύπου αναφοράς (διαστολή και συστολή)
Η μετατροπή τύπου αντικειμένου απεικονίζεται καλύτερα χρησιμοποιώντας ένα δέντρο κληρονομικότητας. Ας δούμε ένα μικρό παράδειγμα κληρονομικότητας:

Γονέας τάξης(
int x;
}

τάξη ChildY επέκταση γονέα (
int y;
}

κλάση ChildZ επέκταση γονέα (
int z;
}

Κάθε τάξη δηλώνει ένα πεδίο με μοναδικό όνομα. Θα εξετάσουμε αυτό το πεδίο ως παράδειγμα ενός συνόλου μοναδικών ιδιοτήτων που είναι εγγενείς σε ορισμένες τύπος αντικειμένου.
Τα αντικείμενα της κλάσης Parent έχουν μόνο ένα πεδίο x, που σημαίνει ότι μόνο οι αναφορές τύπου Parent μπορούν να αναφέρονται σε τέτοια αντικείμενα. Τα αντικείμενα της κλάσης ChildY έχουν ένα πεδίο y και ένα πεδίο x, που κληρονομούνται από την κλάση Parent. Επομένως, τέτοια αντικείμενα μπορούν να υποδεικνύονται με αναφορές τύπου ChildY ή Parent. Παράδειγμα:

Γονέας p = new ChildY();

Σημειώστε ότι μια τέτοια αναφορά p μπορεί να έχει πρόσβαση μόνο στο πεδίο x του δημιουργημένου αντικειμένου. Το πεδίο y δεν είναι διαθέσιμο επειδή ο μεταγλωττιστής, όταν ελέγχει την εγκυρότητα της έκφρασης p.y, δεν μπορεί να προβλέψει ότι η αναφορά p θα δείχνει σε ένα αντικείμενο τύπου ChildY κατά το χρόνο εκτέλεσης. Αναλύει μόνο τον τύπο της ίδιας της μεταβλητής και δηλώνεται ως Γονική, αλλά αυτή η κλάση δεν έχει το πεδίο y, το οποίο θα προκαλέσει σφάλμα μεταγλώττισης.
Ομοίως, τα αντικείμενα της κλάσης ChildZ έχουν ένα πεδίο z και ένα πεδίο x που προέρχονται από την κλάση Parent. Αυτό σημαίνει ότι τέτοια αντικείμενα μπορούν να υποδειχθούν με συνδέσμους όπως ChildZ και Parent.
Έτσι, οι αναφορές του τύπου Parent μπορούν να δείχνουν σε ένα αντικείμενο οποιουδήποτε από τους τρεις εξεταζόμενους τύπους, αλλά οι αναφορές του τύπου ChildY και ChildZ μπορούν να δείχνουν μόνο αντικείμενα ακριβώς του ίδιου τύπου. Τώρα μπορούμε να προχωρήσουμε στη μετατροπή τύπων αναφοράς με βάση αυτό το δέντρο κληρονομικότητας.
Επέκταση σημαίνει μετακίνηση από περισσότερα συγκεκριμένου τύπουστα λιγότερο συγκεκριμένα, δηλ. μετάβαση από τα παιδιά στους γονείς. Παρόμοια με την περίπτωση των πρωτόγονων τύπων, αυτή η μετάβαση γίνεται από το ίδιο το JVM εάν είναι απαραίτητο και είναι «αόρατη» στον προγραμματιστή, δηλαδή δεν απαιτεί ειδικές μετατροπές.

Γονέας p1=new ChildY();
Γονικός p2=new ChildZ();

Και στις δύο γραμμές, στις μεταβλητές τύπου Parent εκχωρείται μια τιμή διαφορετικού τύπου, πράγμα που σημαίνει ότι πραγματοποιείται μετατροπή. Εφόσον πρόκειται για επέκταση, γίνεται αυτόματα και είναι πάντα επιτυχής.
Πρέπει να σημειωθεί ότι κατά τη διάρκεια ενός τέτοιου μετασχηματισμού δεν συμβαίνει τίποτα στο ίδιο το αντικείμενο. Ακόμα κι αν, για παράδειγμα, το πεδίο y της τάξης ChildY δεν είναι πλέον διαθέσιμο, αυτό δεν σημαίνει ότι έχει φύγει. Δεν είναι δυνατή μια τόσο σημαντική αλλαγή στο αντικείμενο. Προήλθε από την κατηγορία ChildY και διατηρεί όλες τις ιδιότητές του. Μόνο ο τύπος του συνδέσμου μέσω του οποίου γίνεται η πρόσβαση στο αντικείμενο έχει αλλάξει.
Η αντίστροφη μετάβαση, δηλαδή η μετακίνηση κάτω από το κληρονομικό δέντρο στους κληρονόμους, είναι μια στένωση. Για παράδειγμα, σε αυτήν την περίπτωση, η μετάβαση από μια αναφορά τύπου Parent, η οποία μπορεί να αναφέρεται σε αντικείμενα τριών κλάσεων, σε μια αναφορά τύπου ChildY, η οποία μπορεί να αναφέρεται μόνο σε μία από τις τρεις κλάσεις, είναι προφανώς περιορισμός. Μια τέτοια μετάβαση μπορεί να μην είναι δυνατή. Εάν μια αναφορά τύπου Parent αναφέρεται σε αντικείμενο τύπου Parent ή ChildZ, τότε δεν είναι δυνατή η μετάβαση στο ChildY, αφού και στις δύο περιπτώσεις το αντικείμενο δεν έχει πεδίο y, το οποίο δηλώνεται στην κλάση ChildY. Επομένως, όταν περιορίζεται, ο σχεδιαστής πρέπει να υποδείξει ρητά ότι είναι απαραίτητο να επιχειρήσει έναν τέτοιο μετασχηματισμό. Το JVM θα ελέγξει ότι η μετάβαση είναι σωστή κατά το χρόνο εκτέλεσης. Εάν είναι δυνατόν, η μετατροπή θα πραγματοποιηθεί. Εάν όχι, θα προκύψει σφάλμα (συνήθως ένα ClassCastException).

Γονέας p=new ChildY();
ChildY cy = (ChildY)p; //σωστά
Γονικός p2=new ChildZ();
ChildY cy2 = (ChildY)p2; //λάθος

Για να ελέγξετε εάν η επιθυμητή μετάβαση είναι δυνατή, μπορείτε να χρησιμοποιήσετε τον τελεστή instanceof:

Γονέας p=new ChildY();
αν (π παράδειγμα ChildY) (
ChildY cy = (ChildY)p;
}

Γονικός p2=new ChildZ();
if (p2 instance of ChildY) (
ChildY cy = (ChildY)p2;
}

Γονικός p3=new Parent();
if (p3 instance of ChildY) (
ChildY cy = (ChildY)p3;
}

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

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

  • Οι τιμές Boole μεταδίδονται στη συμβολοσειρά "true" ή "false" ανάλογα με την τιμή.

  • Για τιμές αντικειμένων, καλείται η μέθοδος toString(). Εάν η μέθοδος επιστρέψει null, το αποτέλεσμα θα είναι η συμβολοσειρά "null".

  • Για μια τιμή null, δημιουργείται η συμβολοσειρά "null".
Απαγορευμένες μεταμορφώσεις
Δεν επιτρέπονται όλες οι μεταβάσεις μεταξύ αυθαίρετων τύπων. Για παράδειγμα, οι απαγορευμένες μετατροπές περιλαμβάνουν: μεταβάσεις από οποιονδήποτε τύπο αναφοράς σε πρωτόγονο τύπο και αντίστροφα (εκτός από τη μετατροπή σε συμβολοσειρά), το boolean μπορεί να μεταδοθεί μόνο σε αυτόν τον τύπο ή σε μια συμβολοσειρά. Επιπλέον, είναι αδύνατο να φέρετε τάξεις που βρίσκονται σε παρακείμενα κλαδιά του δέντρου κληρονομικότητας μεταξύ τους. Στο παράδειγμα που χρησιμοποιείται για την απεικόνιση των τύπων αναφοράς, η μετάβαση από ChildY σε ChildZ απαγορεύεται.
Αυτή η λίστα των απαγορευμένων μετασχηματισμών δεν τελειώνει εκεί. Είναι αρκετά ευρύ και ταυτόχρονα όλες οι επιλογές είναι αρκετά εμφανείς, οπότε δεν θα συζητηθούν λεπτομερώς. Όσοι επιθυμούν μπορούν να λάβουν πλήρη ενημέρωσηαπό την προδιαγραφή.
Φυσικά, η απόπειρα εκτέλεσης παράνομης μετατροπής θα προκαλέσει σφάλμα.

Χρήση εκμαγείων
Οι καταστάσεις για τη χρήση της μετατροπής τύπου μπορούν να ομαδοποιηθούν ως εξής:

  • Εκχώρηση τιμών σε μεταβλητές (ανάθεση). Δεν επιτρέπονται όλες οι μεταβάσεις κατά τη διάρκεια αυτού του μετασχηματισμού - οι περιορισμοί επιλέγονται με τέτοιο τρόπο ώστε να μην μπορεί να προκύψει εξαίρεση.

  • Κλήση μεθόδου. Αυτή η μετατροπή εφαρμόζεται στα ορίσματα της καλούμενης μεθόδου ή κατασκευαστή. Ένα τέτοιο καστ δεν παράγει ποτέ λάθη. Η χύτευση πραγματοποιείται επίσης όταν επιστρέφεται η τιμή της μεθόδου.

  • Ρητό casting. Σε αυτήν την περίπτωση, δηλώνεται ρητά σε ποιον τύπο πρέπει να χυθεί η αρχική τιμή.

  • Ο τελεστής συνένωσης μετατρέπει σε μια συμβολοσειρά των ορισμάτων του.

  • Αριθμητική επέκταση. Οι αριθμητικές πράξεις ενδέχεται να απαιτούν αλλαγή του τύπου του ορίσματος(ων). Αυτή η μετατροπή έχει ένα ειδικό όνομα - εκτεταμένο, καθώς η επιλογή του τύπου στόχου μπορεί να εξαρτάται όχι μόνο από την αρχική τιμή, αλλά και από το δεύτερο όρισμα της λειτουργίας.
Εργασία #8
Προσθέστε στο έργο σας τη χρήση του casting για την ιεραρχία της τάξης σας.

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

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

Υπάρχουν δύο τύποι μετατροπής τύπων στην Java: σιωπηρήΚαι σαφής.

Μετατροπή σιωπηρού τύπουεκτελείται εάν πληρούνται οι ακόλουθες προϋποθέσεις:

  1. Και οι δύο τύποι είναι συμβατοί
  2. Το μήκος του τύπου στόχου είναι μεγαλύτερο ή ίσο με το μήκος του τύπου πηγής

Σε όλες τις άλλες περιπτώσεις θα πρέπει να χρησιμοποιείται μετατροπή ρητού τύπου.

Υπάρχουν επίσης δύο τύποι μετασχηματισμών:

  1. Διεύρυνση μετατροπής
  2. Περιορισμός μετατροπής

Επέκταση μετασχηματισμού ( διευρυνόμενη μετατροπή) εμφανίζεται όταν μια τιμή ενός τύπου μετατρέπεται σε έναν ευρύτερο τύπο, με μεγαλύτερο εύρος αποδεκτές τιμές. Η Java διευρύνει τις μετατροπές αυτόματα, για παράδειγμα εάν αντιστοιχίσετε ένα κυριολεκτικό τύπου int μεταβλητού τύπουδιπλάσιο ή την τιμή μιας μεταβλητής τύπου char τύπου int. Η σιωπηρή μετατροπή έχει πάντα έναν τύπο επέκτασης.

Αλλά αυτό μπορεί να έχει τις δικές του μικρές τσουγκράνες. Για παράδειγμα, εάν μια τιμή int μετατραπεί σε τιμή float. Και η τιμή int στη δυαδική αναπαράσταση έχει περισσότερα από 23 σημαντικά bit, τότε είναι δυνατή η απώλεια ακρίβειας, αφού ο τύπος float έχει 23 bit που έχουν εκχωρηθεί για το ακέραιο τμήμα. Όλα τα χαμηλά bitsΟι τιμές int που δεν χωρούν στα 23 bit του float mantissa θα απορριφθούν, οπότε παρόλο που η σειρά του αριθμού θα διατηρηθεί, η ακρίβεια θα χαθεί. Το ίδιο ισχύει και για τη μετατροπή ενός μακρού τύπου σε διπλό.

Η επέκταση της μετατροπής τύπων Java μπορεί επίσης να απεικονιστεί ως εξής:

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

Αξίζει να εξηγήσουμε λίγο γιατί, για παράδειγμα, ο τύπος byte δεν μετατρέπεται αυτόματα (όχι ρητά) στον τύπο char, αν και ο τύπος byte έχει πλάτος 8 bit και ο χαρακτήρας είναι 16, το ίδιο ισχύει και για τη μετατροπή του ο σύντομος τύπος να χαρακ. Αυτό συμβαίνει επειδή το byte και το short είναι υπογεγραμμένοι τύποι δεδομένων και ο χαρακτήρας δεν είναι υπογεγραμμένος. Επομένως, σε αυτήν την περίπτωση, πρέπει να χρησιμοποιήσετε ένα cast ρητού τύπου, καθώς ο μεταγλωττιστής πρέπει να υποδεικνύει ρητά ότι γνωρίζετε τι θέλετε και πώς θα υποβληθεί σε επεξεργασία το bit πρόσημου του byte και των τύπων σύντομων τύπων κατά τη μετατροπή στον τύπο char.

Η συμπεριφορά μιας τιμής char είναι στις περισσότερες περιπτώσεις η ίδια με τη συμπεριφορά μιας τιμής ακέραιου τύπου, επομένως, μια τιμή char μπορεί να χρησιμοποιηθεί όπου απαιτείται μια τιμή int ή long. Ωστόσο, θυμηθείτε ότι ο τύπος χαρακτήρων είναι ανυπόγραφος, επομένως συμπεριφέρεται διαφορετικά από τον σύντομο τύπο, παρόλο που και οι δύο τύποι έχουν εύρος 16 bit.

μικρός μικρό = ( μικρός ) 0xffff; // Αυτά τα bit αντιπροσωπεύουν τον αριθμό –1
απανθρακώνω ντο = "\uffff"; // Τα ίδια bit αντιπροσωπεύουν τον χαρακτήρα unicode
ενθ i1 = μικρό; // Η μετατροπή του short σε int δίνει –1
ενθ i2 = ντο; // Η μετατροπή char σε int δίνει 65535

Στενικός μετασχηματισμός ( περιοριστική μετατροπή) εμφανίζεται όταν μια τιμή μετατρέπεται σε μια τιμή ενός τύπου του οποίου το εύρος δεν είναι μεγαλύτερο από το αρχικό. Ο περιορισμός των μετατροπών δεν είναι πάντα ασφαλής: για παράδειγμα, η μετατροπή της ακέραιας τιμής 13 σε ένα byte είναι λογική, αλλά η μετατροπή του 13000 σε ένα byte δεν είναι σοφή, καθώς το byte μπορεί να αποθηκεύσει μόνο τους αριθμούς −128 έως 127. Επειδή τα δεδομένα ενδέχεται να χαθούν κατά τη διάρκεια μιας στένωσης μετατροπής, ο μεταγλωττιστής Java αντιτίθεται σε οποιαδήποτε τέτοια μετατροπή, ακόμα κι αν η τιμή που μετατρέπεται εμπίπτει σε ένα στενότερο εύρος του καθορισμένου τύπου:

ενθ Εγώ = 13 ;
ψηφιόλεξη σι = Εγώ ; // Ο μεταγλωττιστής δεν θα επιτρέψει αυτήν την έκφραση

Η μόνη εξαίρεση στον κανόνα είναι όταν εκχωρείτε ένα ολόκληρο literal (μια τιμή int) σε ένα byte ή μια σύντομη μεταβλητή εάν το literal ταιριάζει με το εύρος της μεταβλητής.

Μια περιοριστική μετατροπή είναι πάντα μια μετατροπή σαφούς τύπου.

Ρητή μετατροπή πρωτόγονων τύπων

Ο χειριστής της μετατροπής ρητού τύπου, ή πιο συγκεκριμένα, του τύπου χύτευσης, είναι η χρήση παρενθέσεων, μέσα στις οποίες υποδεικνύεται ο τύπος στον οποίο πραγματοποιείται η μετατροπή - (τύπος). Για παράδειγμα:

ενθ Εγώ = 13 ;
ψηφιόλεξη σι = ( ψηφιόλεξη ) Εγώ ; // Αναγκαστική μετατροπή από int σε byte
Εγώ = ( ενθ ) 13.456 ; // Αναγκαστική μετατροπή του διπλού κυριολεκτικού σε int 13

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

Όταν φέρνετε ένα πιο ευρύχωρο ολόκληρου τύπουστο λιγότερο ευρύχωρο, τα πιο σημαντικά κομμάτια απλώς απορρίπτονται. Ουσιαστικά, αυτό ισοδυναμεί με τη λειτουργία της διαίρεσης του modulo της τιμής που μειώνεται κατά το εύρος του τύπου στόχου (για παράδειγμα, για τον τύπο byte είναι 256).

Πολύ μεγάλο ένας κλασματικός αριθμόςόταν μεταδίδεται σε έναν ακέραιο γίνεται MAX_VALUE ή MIN_VALUE.

Πολύ μεγάλο διπλόόταν φέρεται σε φλοτέρμετατρέπεται σε Float.POSITIVE_INFINITY ή Float.NEGATIVE_INFINITY.

Ο παρακάτω πίνακας είναι ένα πλέγμα όπου, για κάθε πρωτόγονο τύπο, υποδεικνύονται οι τύποι στους οποίους μπορούν να μετατραπούν και η μέθοδος μετατροπής. Γράμμα Νστον πίνακα σημαίνει ότι η μετατροπή δεν είναι δυνατή. Γράμμα Υσημαίνει έναν επεκτεινόμενο μετασχηματισμό που εκτελείται αυτόματα. Γράμμα ΜΕσημαίνει έναν περιορισμένο μετασχηματισμό που απαιτεί ένα ρητό καστ. Τελικά, Υ*σημαίνει έναν αυτόματο μετασχηματισμό διεύρυνσης, κατά τον οποίο η τιμή μπορεί να χάσει μερικά από τα λιγότερο σημαντικά ψηφία της. Αυτό μπορεί να συμβεί κατά τη μετατροπή ενός int ή long σε float ή double. Οι τύποι κινητής υποδιαστολής έχουν μεγαλύτερη εμβέλειααπό τους τύπους ακέραιων αριθμών, οπότε το int ή το long μπορεί να αναπαρασταθεί με float ή double. Ωστόσο, οι τύποι κινητής υποδιαστολής είναι προσεγγίσεις αριθμών και μπορεί να μην περιέχουν πάντα τόσα σημαντικά ψηφία στη μάντισσα όσο ακέραιους τύπους.

Αυτόματη επέκταση τύπου σε εκφράσεις

Αξίζει επίσης να αναφερθεί για άλλη μια φορά για την αυτόματη προώθηση (επέκταση) τύπων σε εκφράσεις. Το έχουμε ήδη αντιμετωπίσει αυτό όταν εξετάσαμε τους τύπους δεδομένων ακεραίων και τις λειτουργίες σε αυτούς, αλλά αξίζει να το θυμηθούμε εδώ για να γίνει ακόμα καλύτερα κατανοητό και, επιπλέον, να σχετίζεται άμεσα με αυτό το θέμα. Στο παράδειγμα κάτω από το σημάδι @ + , , * , / και ούτω καθεξής.

Δηλαδή όλα τα ακέραια κυριολεκτικά σε εκφράσεις, καθώς και τύπους ψηφιόλεξη, μικρόςΚαι απανθρακώνωεπέκταση σε ενθ . Εκτός εάν, όπως περιγράφεται παραπάνω, υπάρχουν άλλοι, μεγαλύτεροι τύποι δεδομένων στην έκφραση ( μακρύς, φλοτέρή διπλό). Επομένως το παραπάνω παράδειγμα θα προκαλέσει σφάλμα μεταγλώττισης επειδή η μεταβλητή ντοέχει τύπο ψηφιόλεξη, και η έκφραση b+1, ως αποτέλεσμα αυτόματης προώθησης, έχει τον τύπο ενθ.

Χύτευση σιωπηρού τύπου σε μικτές εκφράσεις ανάθεσης

Αν και αυτός ο τομέαςκαι αναφέρεται σε σιωπηρή μετατροπή(casting) τύπους, δώσαμε την εξήγησή του εδώ, αφού και σε αυτή την περίπτωση λειτουργεί το ίδιο αυτόματη επέκτασητύπους σε εκφράσεις και, στη συνέχεια, χύτευση σιωπηρού τύπου. Αυτό είναι το σώμα του μπαλέτου. Νομίζω ότι το παρακάτω παράδειγμα θα ξεκαθαρίσει τα πάντα. Όπως και στην προηγούμενη εξήγηση, το σημάδι @ σημαίνει οποιονδήποτε έγκυρο χειριστή, για παράδειγμα + , , * , / και ούτω καθεξής.

Αυτό αξίζει να εξηγηθεί με ένα απλό παράδειγμα:

ψηφιόλεξη β2 = 50 ;
β2 = β2 * 2 ; // δεν θα μεταγλωττιστεί
β2 *= 2 ; //μεταγλωττίζει, αν και ισοδυναμεί με b2 = b2 * 2

Η δεύτερη γραμμή που δίνεται στο παράδειγμα δεν μεταγλωττίζεται λόγω αυτόματης επέκτασης τύπου στις εκφράσεις, αφού η έκφραση b2*2 είναι τύπου int, αφού συμβαίνει αυτόματη επέκταση τύπου (τα ακέραια γράμματα στην έκφραση είναι πάντα int). Η τρίτη γραμμή θα μεταγλωττιστεί εύκολα, αφού το σιωπηρό casting στη συνδυασμένη έκφραση ανάθεσης θα λειτουργήσει σε αυτήν.

Boxing/unboxing - μετατροπή πρωτόγονων τύπων σε αντικείμενα περιτυλίγματος

Η πυγμαχία και το unboxing είναι επίσης ένα αρκετά μεγάλο θέμα, αλλά είναι αρκετά απλό.

Ουσιαστικά Το boxing και το unboxing είναι μετατροπές από πρωτόγονους τύπους σε αντικείμενα περιτυλίγματος και πίσω.

Για αντικείμενα περιτυλίγματος πρωτόγονων τύπων ισχύουν όλα όσα ειπώθηκαν παραπάνω.

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

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

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

Επιτρέψτε μου να σας δώσω ένα απλό παράδειγμα:

ενθ i3 ;
ψηφιόλεξη β2 = 3 ;
Ψηφιόλεξη myB ;
myB= β2;
myB++;
β2= myB;
i3= myB;

Εάν δεν είναι ακόμη σαφές γιατί χρειάζεται αυτό, τότε δεν είναι τρομακτικό, απλά δέστε έναν κόμπο για τη μνήμη.

Κάθε έκφραση στην Java έχει έναν τύπο, ο οποίος καθορίζεται από τη δομή της έκφρασης και τους τύπους των τελεστών που την αποτελούν (σταθερές, μεταβλητές και μεθόδους). Ωστόσο, μερικές φορές μπορεί να χρειαστεί να μετατρέψουμε ρητά μια έκφραση σε άλλο τύπο. Επιπλέον, σε ορισμένες περιπτώσεις το ίδιο το σύστημα χρόνου εκτέλεσης Java εκτελεί έμμεσα τέτοιες μετατροπές.

Μετατροπή τύπουΤο T 1 to type T 2 επιτρέπει σε μια έκφραση του τύπου T 1 να αντιμετωπιστεί κατά το χρόνο μεταγλώττισης ως έκφραση του τύπου T 2 . Σε ορισμένες περιπτώσεις πρόκειται για μια καθαρά συντακτική κατασκευή που δεν επηρεάζει τον κώδικα που δημιουργείται, σε άλλες μια μετατροπή τύπου απαιτεί πρόσθετες ενέργειεςκατά την εκτέλεση για αλλαγή της τιμής μιας έκφρασης ή πρόσθετοι έλεγχοι για την ορθότητα του εφαρμοσμένου μετασχηματισμού. Παραδείγματα:

  • Μετατροπή τύπου ενθσε τύπο μακρύςαπαιτεί επέκταση του πρόσημου μιας ακέραιας τιμής 32-bit σε έναν ακέραιο αριθμό 64-bit κατά την εκτέλεση του προγράμματος. Δεν υπάρχει απώλεια πληροφοριών.
  • Μετατροπή τύπου φλοτέρσε τύπο μακρύςαπαιτεί μια μη τετριμμένη μετατροπή από μια κινητή τιμή 32-bit σε έναν ακέραιο αριθμό 64-bit κατά την εκτέλεση του προγράμματος. Ανάλογα με την αρχική τιμή, μπορεί να συμβεί απώλεια πληροφοριών ή όχι.
  • Η μετατροπή ενός τύπου Thread σε τύπο Object δεν απαιτεί καμία ενέργεια: Επειδή η κλάση Thread είναι απόγονος της κλάσης Object, οποιαδήποτε αναφορά σε ένα αντικείμενο του τύπου Thread είναι αυτόματα αναφορά σε ένα αντικείμενο του τύπου Object .
  • Η μετατροπή από τύπο αντικειμένου σε τύπο νήματος απαιτεί έλεγχο κατά το χρόνο εκτέλεσης. Εάν η αναφορά που μετατρέπεται είναι πράγματι μια αναφορά σε ένα αντικείμενο τύπου Thread, τότε επιστρέφεται ως αποτέλεσμα της μετατροπής· διαφορετικά, δημιουργείται μια εξαίρεση.

5.4.1.1. Διευρυνόμενοι μετασχηματισμοί αριθμών

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

  • μεταμόρφωση ψηφιόλεξη V μικρός, ενθ, μακρύς, φλοτέρΚαι διπλό;
  • μεταμόρφωση μικρός V ενθ, μακρύς, φλοτέρΚαι διπλό;
  • μεταμόρφωση απανθρακώνω V ενθ, μακρύς, φλοτέρΚαι διπλό;
  • μεταμόρφωση ενθ V μακρύς, φλοτέρΚαι διπλό;
  • μεταμόρφωση μακρύς V φλοτέρΚαι διπλό;
  • μεταμόρφωση φλοτέρ V διπλό.

Στην πραγματικότητα, η μετατροπή μιας ακέραιας τιμής σε μια κινητή τιμή μπορεί να οδηγήσει σε απώλεια ακρίβειας, δηλ. απώλεια σημαντικών ψηφίων. Λοιπόν, το ακόλουθο παράδειγμα

Δοκιμή τάξης (δημόσιο στατικό κενό main (String args) ( int bigNumber = 1234567890; float approximate = bigNumber; System.out.println (κατά προσέγγιση); ) )

θα εμφανίσει τη συμβολοσειρά 1234567936. Αυτό οφείλεται στο γεγονός ότι κατά τη μετατροπή ενθ V φλοτέρη προκύπτουσα τιμή είναι 1,2345679E9 λόγω του γεγονότος ότι η μάντισσα αριθμών όπως φλοτέρκρατάει μόνο 8 δεκαδικά ψηφία(εδώ για σωστή λειτουργίαπρέπει να χρησιμοποιηθεί μετατροπή τύπου διπλό). Ωστόσο, το σύστημα χρόνου εκτέλεσης δεν δημιουργεί ποτέ σφάλματα κατά την εκτέλεση αυτών των μετασχηματισμών.

5.4.1.2. Περιορισμός αριθμών μετατροπών

Περιορισμός αριθμών μετατροπώνΠρόκειται για μετατροπές από έναν αριθμητικό τύπο σε έναν "μικρότερο" αριθμητικό τύπο, που μπορεί να οδηγήσει σε απώλεια μεγέθους και σε απώλεια ακρίβειας. Τέτοιες μετατροπές στην Java είναι:

  • μεταμόρφωση ψηφιόλεξη V απανθρακώνω;
  • μεταμόρφωση μικρός V ψηφιόλεξηΚαι απανθρακώνω;
  • μεταμόρφωση ενθ V ψηφιόλεξη, μικρόςΚαι απανθρακώνω;
  • μεταμόρφωση μακρύς V ψηφιόλεξη, μικρός, ενθΚαι απανθρακώνω;
  • μεταμόρφωση φλοτέρ V ψηφιόλεξη, μικρός, ενθ, μακρύςΚαι απανθρακώνω;
  • μεταμόρφωση διπλό V ψηφιόλεξη, μικρός, ενθ, μακρύς, φλοτέρΚαι απανθρακώνω;

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

5.4.1.3. Επέκταση μετασχηματισμών συνδέσμων

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

  • μετατροπή οποιασδήποτε κλάσης ή διεπαφής στον πρόγονό της (ιδίως στον τύπο Αντικειμένου).
  • μετατροπή μιας κλάσης στη διεπαφή που υλοποιεί·
  • μετατροπή οποιουδήποτε πίνακα σε τύπο αντικειμένου ή τύπο Cloneable.
  • Η μετατροπή ενός πίνακα τύπου S σε πίνακα τύπου T εάν τα S και T είναι τύποι αναφοράς και η μετατροπή S σε T διευρύνεται.
  • Μετατροπή μηδενικού τύπου σε οποιονδήποτε τύπο αναφοράς.

5.4.1.4. Μετασχηματισμοί στενών συνδέσμων

Μετασχηματισμοί στενών συνδέσμωνΠρόκειται για μετατροπές παραγόμενων τύπων αναφοράς σε τύπους των απογόνων τους. Αυτές οι μετατροπές απαιτούν επικύρωση χρόνου εκτέλεσης της νομιμότητάς τους και ενδέχεται να δημιουργήσουν ένα ClassCastException. Τέτοιες μετατροπές στην Java είναι:

  • μετατροπή οποιασδήποτε κλάσης στον απόγονό της (συγκεκριμένα, μετατροπή του τύπου Object σε οποιαδήποτε άλλη κλάση).
  • μετατροπή μιας κλάσης σε διεπαφή όταν η κλάση δεν είναι τελική και δεν υλοποιεί τη διεπαφή (ιδίως, μετατροπή του τύπου αντικειμένου σε οποιαδήποτε διεπαφή).
  • μετατροπή τύπου αντικειμένου σε οποιονδήποτε πίνακα.
  • μετατροπή οποιασδήποτε διεπαφής σε κλάση που δεν είναι τελική.
  • μετατροπή οποιασδήποτε διεπαφής σε κλάση που είναι τελική και υλοποιεί τη δεδομένη διεπαφή.
  • μετατροπή της διεπαφής J σε διασύνδεση K όταν ο J δεν είναι παιδί του K και δεν υπάρχει μέθοδος που δηλώνεται τόσο στο J όσο και στο K με την ίδια υπογραφή, αλλά ΔΙΑΦΟΡΕΤΙΚΟΙ ΤΥΠΟΙαποτέλεσμα;
  • η μετατροπή ενός πίνακα τύπου S σε πίνακα τύπου T εάν τα S και T είναι τύποι αναφοράς και η μετατροπή S σε T στενεύει.

5.4.1.5. Μετατροπές σε χορδές

Οποιαδήποτε έκφραση σε Java, συμπεριλαμβανομένων μηδενικό, μπορεί να μετατραπεί σε τύπο συμβολοσειράς.

5.4.1.6. Μη έγκυρες μετατροπές

Οι ακόλουθες μετατροπές τύπων απαγορεύονται στην Java:

  • μετατροπή οποιουδήποτε τύπου αναφοράς σε οποιονδήποτε πρωτόγονο τύπο.
  • μετατροπή οποιουδήποτε πρωτόγονου τύπου σε οποιονδήποτε τύπο αναφοράς εκτός από το String.
  • μετατροπή ενός μηδενικού τύπου σε οποιονδήποτε πρωτόγονο τύπο.
  • μετατροπές σε μηδενικό τύπο ή τύπο boolean;
  • μετατροπές τύπων booleanσε οποιονδήποτε τύπο εκτός από το String ;
  • μετατροπή μιας κλάσης σε άλλη εάν καμία δεν είναι πρόγονος της άλλης (εκτός από τη μετατροπή σε String).
  • μετατροπή μιας κλάσης σε διεπαφή εάν η κλάση είναι τελική και δεν υλοποιείται αυτή τη διεπαφή;
  • μετατροπή μιας κλάσης σε πίνακα εάν η κλάση είναι διαφορετική από το αντικείμενο.
  • μετατροπή μιας διεπαφής σε μια κλάση που είναι τελική και δεν υλοποιεί τη δεδομένη διεπαφή (εκτός από τη μετατροπή σε τύπο συμβολοσειράς).
  • μετατροπή της διεπαφής J σε διασύνδεση K εάν υπάρχει μια μέθοδος που δηλώνεται τόσο στο J όσο και στο K με την ίδια υπογραφή αλλά διαφορετικούς τύπους αποτελεσμάτων.
  • μετατροπή ενός πίνακα σε κλάση διαφορετική από το Object και το String.
  • μετατροπή ενός πίνακα σε διεπαφή που δεν μπορεί να κλωνοποιηθεί.
  • η μετατροπή ενός πίνακα τύπου S σε πίνακα τύπου T εάν η μετατροπή S σε T είναι παράνομη

5.4.2. Μετασχηματιστικά πλαίσια

5.4.2.1. Μετατροπή κατόπιν αποστολής

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

  • η μεταβλητή έχει τύπο ψηφιόλεξη, μικρόςή απανθρακώνω;
  • η τιμή της έκφρασης είναι μια σταθερά τύπου ενθ, το οποίο εμπίπτει στο εύρος των πιθανών τιμών της μεταβλητής.

Για παράδειγμα, ο τελεστής byte x = 123; αποδεκτό, αφού η σταθερά 123 (τύπου ενθ) βρίσκεται εντός του εύρους των αποδεκτών τιμών του τύπου ψηφιόλεξη.

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

Σύντομο s = 123; char c = s; // δημιουργεί ένα σφάλμα μεταγλώττισης

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

5.4.2.2. Επιχειρήματα μεθόδου μετατροπής

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

Δοκιμή τάξης ( static int m(byte a, int b) ( return a + b; ) static int m(short a, short b) ( return a - b; ) public static void main (String args) ( System.out. println( m(1, 2)) // δημιουργεί ένα σφάλμα μεταγλώττισης ) )

Εδώ η κλάση Test περιέχει δύο μεθόδους με το ίδιο όνομα, οι οποίες διαφέρουν μόνο στους τύπους των παραμέτρων. Εάν επιτρέπονταν οι περιοριστικές μετατροπές ορισμών στην Java, ο χρόνος εκτέλεσης θα έπρεπε να καθορίσει σε ποιες από αυτές τις μεθόδους ισχύει η κλήση στο m(1, 2). Για να αποφευχθούν τέτοιες ασάφειες, οι προγραμματιστές γλωσσών έλυσαν το πρόβλημα ριζικά: απαγόρευσαν τέτοιες κλήσεις μεθόδων. Σε αυτήν την περίπτωση, για να καλέσουμε, για παράδειγμα, την πρώτη μέθοδο, πρέπει να υποδείξουμε ρητά τον τύπο του πρώτου τελεστή (ο δεύτερος από προεπιλογή έχει ήδη τον τύπο ενθ), δηλαδή m((byte)1, 2) .

5.4.2.3. Μετατροπή σε συμβολοσειρά

Μετατροπή σε συμβολοσειράεμφανίζεται μόνο σε μία περίπτωση: όταν η πράξη δυαδικού + εφαρμόζεται σε δύο τελεστές, ο ένας από τους οποίους είναι τύπου String. Σε αυτήν την περίπτωση, ο δεύτερος τελεστής μετατρέπεται επίσης σε τύπο String και το αποτέλεσμα της πράξης είναι μια συνένωση των συμβολοσειρών που προκύπτουν. Αυτή η διαδικασία περιγράφεται λεπτομερέστερα στο Κεφ. 5.14.

5.4.2.4. Μετατροπή ρητού τύπου

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

5.4.3. Αριθμητικές μετατροπές τύπου τελεστών

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

Πριν εκτελέσετε μια ενιαία λειτουργία:

  • αν ο τελεστής είναι τύπου ψηφιόλεξη, μικρόςή απανθρακώνω, μετατρέπεται σε τύπο ενθ;
  • σε άλλες περιπτώσεις ο τύπος του δεν αλλάζει.

Πριν εκτελέσετε μια δυαδική λειτουργία:

  • εάν ένας από τους τελεστές είναι τύπου διπλό, τότε το δεύτερο μετατρέπεται επίσης σε τύπο διπλό;
  • φλοτέρ, τότε το δεύτερο μετατρέπεται επίσης σε τύπο φλοτέρ;
  • Διαφορετικά, εάν ένας από τους τελεστές είναι τύπου μακρύς, τότε το δεύτερο μετατρέπεται επίσης σε τύπο μακρύς;
  • Διαφορετικά, και οι δύο τελεστές μετατρέπονται σε πληκτρολόγηση ενθ.

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

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

byte b=3; int a=b;

Στην τελευταία γραμμή, η τιμή της μεταβλητής byte b θα μετατραπεί αυτόματα στον τύπο της μεταβλητής a (δηλαδή int ) και δεν χρειάζεται να γίνουν ειδικές ενέργειες για αυτό.

Οι παρακάτω 19 μετασχηματισμοί είναι επέκτασης:

  • από byte σε short, int, long, float, double
  • από short σε int, long, float, double
  • από char σε int, long, float, double
  • από int σε long, float, double
  • από μακρύ σε float, διπλό
  • από float σε διπλό

Λάβετε υπόψη ότι δεν μπορείτε να μετατρέψετε σε χαρακτήρες από τύπους μικρότερου ή ίσου μήκους (byte, short) ή, αντίθετα, σε short από char χωρίς απώλεια δεδομένων. Αυτό οφείλεται στο γεγονός ότι το char, σε αντίθεση με άλλους ακέραιους τύπους, είναι ανυπόγραφο.

Ωστόσο, πρέπει να θυμόμαστε ότι ακόμη και με την επέκταση, τα δεδομένα μπορούν να παραμορφωθούν σε ειδικές περιπτώσεις. Έχουν ήδη συζητηθεί στην προηγούμενη διάλεξη, πρόκειται για casting int σε float type και casting long values ​​to float ή double type. Αν και αυτοί οι κλασματικοί τύποι μπορούν να φιλοξενήσουν πολύ μεγαλύτερους αριθμούς από τους αντίστοιχους ακέραιους αριθμούς, έχουν λιγότερα σημαντικά ψηφία.

Ας επαναλάβουμε αυτό το παράδειγμα:

μήκος a=111111111111L; float f = a; a = (μακρύ) f; print(a);

Το αποτέλεσμα θα είναι:

Ο αντίστροφος μετασχηματισμός - στένωση - σημαίνει ότι η μετάβαση πραγματοποιείται από έναν πιο ευρύχωρο τύπο σε έναν λιγότερο χωρητικό. Με αυτή τη μετατροπή υπάρχει κίνδυνος απώλειας δεδομένων. Για παράδειγμα, εάν ένας αριθμός int ήταν μεγαλύτερος από 127, τότε κατά τη μετατροπή του σε byte, οι τιμές των bit υψηλότερων από το όγδοο θα χαθούν. Στην Java, μια τέτοια μετατροπή πρέπει να γίνεται ρητά, δηλ. Ο προγραμματιστής πρέπει να αναφέρει ρητά στον κώδικα ότι σκοπεύει να πραγματοποιήσει μια τέτοια μετατροπή και ότι είναι διατεθειμένος να χάσει δεδομένα.

Οι παρακάτω μετασχηματισμοί στενεύουν:

  • από byte σε χαρακτήρες
  • από σύντομο σε byte, χαρ
  • από char σε byte, σύντομο
  • από int σε byte, σύντομο, char
  • από μακρύ σε byte, short, char, int
  • από float σε byte, short, char, int, long
  • από διπλό σε byte, short, char, int, long, float

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

print((byte)383); print((byte)384); print((byte)-384);

Το αποτέλεσμα θα είναι:

Μπορεί να φανεί ότι το bit πρόσημο δεν είχε κανένα αποτέλεσμα κατά τη στένωση, αφού απλώς απορρίφθηκε - το αποτέλεσμα της εύρεσης αντίθετων αριθμών (384 και -384) αποδείχθηκε το ίδιο. Κατά συνέπεια, μπορεί να χαθεί όχι μόνο η ακριβής απόλυτη τιμή, αλλά και το πρόσημο του μεγέθους.

Αυτό ισχύει επίσης για τον τύπο χαρακτήρων:

char c=40000; print((σύντομη)c);

Το αποτέλεσμα θα είναι:

Στένωση κλασματικός τύποςσε έναν ακέραιο είναι μια πιο περίπλοκη διαδικασία. Πραγματοποιείται σε δύο στάδια.

Στο πρώτο βήμα, η κλασματική τιμή μετατρέπεται σε long εάν ο τύπος στόχος είναι long, ή σε int διαφορετικά ( τύπος στόχου byte, short, char ή int). Για να γίνει αυτό, ο αρχικός κλασματικός αριθμός στρογγυλοποιείται πρώτα μαθηματικά προς το μηδέν, δηλαδή το κλασματικό μέρος απλώς απορρίπτεται.

Για παράδειγμα, ο αριθμός 3,84 θα στρογγυλοποιηθεί στο 3 και το -3,84 θα γίνει -3. Σε αυτή την περίπτωση, μπορεί να προκύψουν ειδικές περιπτώσεις:

  • εάν η αρχική κλασματική τιμή είναι NaN, τότε το αποτέλεσμα του πρώτου βήματος θα είναι 0 του επιλεγμένου τύπου (δηλ. int ή long).
  • εάν η αρχική κλασματική τιμή είναι θετική ή αρνητική άπειρο, τότε το αποτέλεσμα του πρώτου βήματος θα είναι, αντίστοιχα, το μέγιστο ή το ελάχιστο πιθανό νόημαγια τον επιλεγμένο τύπο (δηλαδή για int ή long).
  • τέλος, εάν η κλασματική τιμή ήταν μια τελική τιμή, αλλά ως αποτέλεσμα της στρογγυλοποίησης ο αριθμός ήταν πολύ μεγάλος σε απόλυτη τιμή για τον επιλεγμένο τύπο (δηλαδή για int ή long ), τότε, όπως στην προηγούμενη παράγραφο, το αποτέλεσμα της πρώτης βήμα θα είναι, αντίστοιχα, η μέγιστη ή η ελάχιστη δυνατή τιμή αυτού του τύπου. Εάν το αποτέλεσμα στρογγυλοποίησης εμπίπτει στο εύρος τιμών του επιλεγμένου τύπου, τότε θα είναι το αποτέλεσμα του πρώτου βήματος.
  • και int είναι αρκετά προφανείς - τα κλασματικά άπειρα έχουν μετατραπεί, αντίστοιχα, στις ελάχιστες και μέγιστες δυνατές τιμές αυτών των τύπων. Το αποτέλεσμα για τους επόμενους τρεις τύπους (short, char, byte) είναι ουσιαστικά ένας περαιτέρω περιορισμός των τιμών που λαμβάνονται για το int, σύμφωνα με το δεύτερο βήμα της διαδικασίας μετατροπής. Και αυτό γίνεται, όπως περιγράφεται, απλώς απορρίπτοντας τα πιο σημαντικά κομμάτια. Θυμηθείτε ότι η ελάχιστη δυνατή τιμή σε μορφή bit αντιπροσωπεύεται ως 1000..000 (μόνο 32 bit για το int, δηλαδή ένα και 31 μηδέν). Το μέγιστο δυνατό είναι 1111..111 (31 μονάδες). Απορρίπτοντας τα πιο σημαντικά bits, παίρνουμε το αποτέλεσμα 0 για αρνητικό άπειρο, το οποίο είναι το ίδιο και για τους τρεις τύπους. Για θετικό άπειρο, παίρνουμε ένα αποτέλεσμα στο οποίο όλα τα bits είναι ίσα με 1

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