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

Γιατί Kotlin; Οκτώ δυνατότητες που θα μπορούσαν να πείσουν τους προγραμματιστές Java να αλλάξουν

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

Το ιστορικό κυκλοφορίας του Kotlin

Το Kotlin ανακοινώθηκε το 2011, αλλά η πρώτη σταθερή έκδοση, έκδοση 1.0, δεν εμφανίστηκε μέχρι το 2016. Η γλώσσα είναι δωρεάν και ανοιχτού κώδικα, που αναπτύχθηκε από την JetBrains με τον Andrey Breslav να λειτουργεί ως ο κύριος σχεδιαστής γλωσσών. Το Kotlin 1.3.40 κυκλοφόρησε τον Ιούνιο του 2019.

Σχετικά με τον Κότλιν

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

  1. Καθαρή, συμπαγής σύνταξη
  2. Σύστημα ενός τύπου (σχεδόν)
  3. Μηδενική ασφάλεια
  4. Λειτουργίες και λειτουργικός προγραμματισμός
  5. Κατηγορίες δεδομένων
  6. Επεκτάσεις
  7. Υπερφόρτωση χειριστή
  8. Αντικείμενα ανώτατου επιπέδου και το μοτίβο Singleton

Γειά σου Κόσμε! Kotlin εναντίον Java

Η λίστα 1 δείχνει την υποχρεωτική "Γεια, κόσμος!" συνάρτηση γραμμένη στο Kotlin.

Λίστα 1. "Γεια, κόσμος!" στο Κότλιν

 fun main () {println ("Γεια, κόσμος!")} 

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

  1. κύριος είναι μια λειτουργία ανώτατου επιπέδου. Δηλαδή, οι λειτουργίες Kotlin δεν χρειάζεται να τοποθετηθούν μέσα σε μια τάξη.
  2. Δεν υπάρχουν δημόσιο στατικό τροποποιητές. Ενώ το Kotlin έχει τροποποιητές ορατότητας, η προεπιλογή είναι δημόσιο και μπορεί να παραλειφθεί. Το Kotlin επίσης δεν υποστηρίζει το στατικός τροποποιητή, αλλά δεν απαιτείται σε αυτήν την περίπτωση επειδή κύριος είναι μια λειτουργία ανώτατου επιπέδου.
  3. Από το Kotlin 1.3, η παράμετρος array-of-string για κύριος δεν απαιτείται και μπορεί να παραλειφθεί εάν δεν χρησιμοποιηθεί. Εάν χρειαστεί, θα δηλωθεί ως υποστηρίζει: Array.
  4. Δεν έχει καθοριστεί τύπος επιστροφής για τη συνάρτηση. Πού χρησιμοποιεί η Java κενός, Χρησιμοποιεί ο Kotlin Μονάδα, και εάν ο τύπος επιστροφής μιας συνάρτησης είναι Μονάδα, μπορεί να παραλειφθεί.
  5. Δεν υπάρχουν ερωτηματικά σε αυτήν τη λειτουργία. Στο Kotlin, τα ερωτηματικά είναι προαιρετικά και επομένως οι αλλαγές γραμμής είναι σημαντικές.

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

1. Καθαρότερη, πιο συμπαγής σύνταξη

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

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

Πληκτρολογήστε συμπεράσματα

Στο Kotlin μπορείτε να δηλώσετε μια μεταβλητή ως var x: Int = 5ή μπορείτε να χρησιμοποιήσετε τη συντομότερη αλλά εξίσου σαφή έκδοση var x = 5. (Ενώ η Java υποστηρίζει τώρα var δηλώσεις, το χαρακτηριστικό αυτό δεν εμφανίστηκε μέχρι την Java 10, πολύ μετά την εμφάνιση του χαρακτηριστικού στο Kotlin.)

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

Λίστα 2. Μεταβλητές μόνο για ανάγνωση στο Kotlin

 val x = 5 ... x = 6 // ΣΦΑΛΜΑ: ΔΕΝ ΣΥΜΠΛΗΡΩΘΕΙ 

Ιδιότητες έναντι πεδίων

Όπου η Java έχει πεδία, το Kotlin έχει ιδιότητες. Οι ιδιότητες δηλώνονται και προσπελάζονται με τρόπο παρόμοιο με τα δημόσια πεδία στην Java, αλλά το Kotlin παρέχει προεπιλεγμένες υλοποιήσεις των λειτουργιών πρόσβασης / μεταλλάκτη για ιδιότητες. Δηλαδή, παρέχει ο Kotlin παίρνω() λειτουργίες για βαλ ιδιότητες και τα δύο παίρνω() και σειρά() λειτουργίες για var ιδιότητες. Προσαρμοσμένες εκδόσεις του παίρνω() και σειρά() μπορεί να εφαρμοστεί όταν είναι απαραίτητο.

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

Προεπιλογή έναντι ρητών εισαγωγών

Η Java εισάγει σιωπηρά τάξεις που ορίζονται στο πακέτο java.lang, αλλά όλες οι άλλες κατηγορίες πρέπει να εισάγονται ρητά. Ως αποτέλεσμα, πολλά αρχεία προέλευσης Java ξεκινούν εισάγοντας κλάσεις συλλογής από java.util, Τάξεις I / O από java.io, και ούτω καθεξής. Από προεπιλογή, το Kotlin εισάγει σιωπηρά Κότλιν. *, το οποίο είναι περίπου ανάλογο με την εισαγωγή Java java.lang. *, αλλά και η Kotlin εισάγει kotlin.io. *, kotlin.collections. *και τάξεις από πολλά άλλα πακέτα. Εξαιτίας αυτού, τα αρχεία προέλευσης Kotlin συνήθως απαιτούν λιγότερες σαφείς εισαγωγές από τα αρχεία προέλευσης Java, ειδικά για τάξεις που χρησιμοποιούν συλλογές ή / και τυπικό I / O.

Χωρίς κλήση για «νέο» για κατασκευαστές

Στο Kotlin, η λέξη-κλειδί νέος δεν απαιτείται για τη δημιουργία ενός νέου αντικειμένου. Για να καλέσετε έναν κατασκευαστή, απλώς χρησιμοποιήστε το όνομα της τάξης με παρενθέσεις. Ο κώδικας Java

 Φοιτητής s = νέος Φοιτητής (...); // ή var s = νέος μαθητής (...); 

θα μπορούσε να γραφτεί ως εξής στο Kotlin:

 var s = Μαθητής (...) 

Πρότυπα συμβολοσειράς

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

 println ("Όνομα:" + όνομα + ", Τμήμα:" + dept); 

θα μπορούσε να αντικατασταθεί από τον μικρότερο αλλά ισοδύναμο κωδικό Kotlin.

 println ("Όνομα: $ όνομα, Τμήμα: $ dept") 

Επεκτείνει και υλοποιεί

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

 Δημόσια τάξη Ο μαθητής επεκτείνει Συγκρίσιμα εργαλεία 

θα γράφτηκε πιο απλά στο Kotlin ως εξής:

 μαθητής τάξης: Πρόσωπο, συγκρίσιμο 

Δεν υπάρχουν ελεγμένες εξαιρέσεις

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

Καταστροφή

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

Λίστα 3. Παραδείγματα καταστροφής

 val { και η τιμή} 

δηλώσεις και εκφράσεις «if»

Στο Κότλιν, αν μπορεί να χρησιμοποιηθεί για έλεγχο ροής όπως με την Java, αλλά μπορεί επίσης να χρησιμοποιηθεί ως έκφραση. Τριαδικός τελεστής της Java (?:αντικαθίσταται από το καθαρότερο αλλά κάπως μακρύτερο αν έκφραση. Για παράδειγμα, ο κώδικας Java

 διπλό max = x> = y; x: ε 

θα γράφτηκε στο Kotlin ως εξής:

val max = if (x> = y) τότε x άλλο y 

Το Kotlin είναι ελαφρώς πιο ρητό από το Java σε αυτήν την περίπτωση, αλλά η σύνταξη είναι αναμφισβήτητα πιο ευανάγνωστη.

"when" αντικαθιστά "διακόπτη"

Η λιγότερο αγαπημένη μου δομή ελέγχου σε γλώσσες τύπου C είναι η διακόπτης δήλωση. Το Kotlin αντικαθιστά το διακόπτης δήλωση με ένα πότε δήλωση. Η καταχώριση 4 προέρχεται απευθείας από την τεκμηρίωση του Kotlin. Σημειώσε ότι Διακοπή Δεν απαιτούνται δηλώσεις και μπορείτε εύκολα να συμπεριλάβετε εύρη τιμών.

Λίστα 4. Δήλωση «όταν» στο Kotlin

 όταν (x) {in 1..10 -> print ("x is in the range") in validNumbers -> print ("x is valid")! in 10..20 -> print ("x είναι εκτός του εύρους ") αλλιώς -> εκτύπωση (" κανένα από τα παραπάνω ")} 

Δοκιμάστε να ξαναγράψετε την καταχώριση 4 ως ένα παραδοσιακό C / Java διακόπτης δήλωση, και θα πάρετε μια ιδέα για το πόσο καλύτερα είμαστε με το Kotlin's πότε δήλωση. Επίσης, παρόμοιο με αν, πότε μπορεί να χρησιμοποιηθεί ως έκφραση. Σε αυτήν την περίπτωση, η τιμή του ικανοποιημένου κλάδου γίνεται η αξία της συνολικής έκφρασης.

Εναλλαγή εκφράσεων σε Java

Η Java 12 παρουσίασε εκφράσεις διακόπτη. Παρόμοιο με το Kotlin's πότε, Οι εκφράσεις εναλλαγής Java δεν απαιτούν Διακοπή δηλώσεις και μπορούν να χρησιμοποιηθούν ως δηλώσεις ή εκφράσεις. Ανατρέξτε στην ενότητα "Loop, switch ή κάντε ένα διάλειμμα; Αποφάσεις και επαναλήψεις με δηλώσεις" για περισσότερες πληροφορίες σχετικά με τις εκφράσεις διακόπτη στην Java.

2. Σύστημα ενός τύπου (σχεδόν)

Η Java διαθέτει δύο χωριστά συστήματα τύπων, πρωτόγονους τύπους και τύπους αναφοράς (π.χ. αντικείμενα). Υπάρχουν πολλοί λόγοι για τους οποίους η Java περιλαμβάνει δύο ξεχωριστά συστήματα τύπου. Στην πραγματικότητα αυτό δεν είναι αλήθεια. Όπως περιγράφεται στο άρθρο μου Μια περίπτωση για τη διατήρηση πρωτόγονων στην Java, υπάρχει πραγματικά μόνο ένας λόγος για πρωτόγονους τύπους - απόδοση. Παρόμοια με τη Scala, το Kotlin διαθέτει μόνο ένα σύστημα τύπου, δεδομένου ότι ουσιαστικά δεν υπάρχει διάκριση μεταξύ πρωτόγονων τύπων και τύπων αναφοράς στο Kotlin. Το Kotlin χρησιμοποιεί πρωτόγονους τύπους όταν είναι δυνατόν, αλλά θα χρησιμοποιήσει αντικείμενα εάν είναι απαραίτητο.

Γιατί λοιπόν η προειδοποίηση του "σχεδόν"; Επειδή το Kotlin διαθέτει επίσης εξειδικευμένες τάξεις για την απεικόνιση συστοιχιών πρωτόγονων τύπων χωρίς την αυτόματη επιβάρυνση: IntArray, DoubleArray, και ούτω καθεξής. Στο JVM, DoubleArray εφαρμόζεται ως διπλό[]. Χρησιμοποιεί DoubleArray πραγματικά κάνεις τη διαφορά; Ας δούμε.

Συγκριτική αξιολόγηση 1: Πολλαπλασιασμός μήτρας

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

Λίστα 5. Πολλαπλασιασμός Matrix στο Kotlin

 fun multiply (a: Array, b: Array): Array {if (! checkArgs (a, b)) throw Exception ("Οι πίνακες δεν είναι συμβατές για πολλαπλασιασμό") val nRows = a.size val nCols = b [0]. size val result = Array (nRows, {_ -> DoubleArray (nCols, {_ -> 0,0})}) για (rowNum σε 0 έως nRows) {για (colNum σε 0 έως nCols) {var sum = 0,0 για (i σε 0 έως ένα άθροισμα [0] .size] + = a [rowNum] [i] * b [i] [colNum] αποτέλεσμα [rowNum] [colNum] = sum}} αποτέλεσμα επιστροφής} 

Στη συνέχεια, συνέκρινα την απόδοση των δύο εκδόσεων Kotlin με αυτήν της Java με διπλό και Java με Διπλό, τρέχοντας και τα τέσσερα σημεία αναφοράς στον τρέχοντα φορητό υπολογιστή μου. Δεδομένου ότι υπάρχει μικρή ποσότητα "θορύβου" στην εκτέλεση κάθε κριτηρίου αναφοράς, έτρεξα όλες τις εκδόσεις τρεις φορές και κατά μέσο όρο τα αποτελέσματα, τα οποία συνοψίζονται στον Πίνακα 1.

Πίνακας 1. Απόδοση χρόνου εκτέλεσης του κριτηρίου πολλαπλασιασμού μήτρας

Χρονικά αποτελέσματα (σε δευτερόλεπτα)
Ιάβα

(διπλό)

Ιάβα

(Διπλό)

Κότλιν

(DoubleArray)

Κότλιν

(Πίνακας)

7.3029.836.8115.82

Ήμουν κάπως έκπληκτος από αυτά τα αποτελέσματα, και σχεδιάζω δύο takeaways. Πρώτον, η απόδοση του Kotlin χρησιμοποιεί DoubleArray είναι σαφώς ανώτερο από την απόδοση του Kotlin Πίνακας, που είναι σαφώς ανώτερο από αυτό της Java χρησιμοποιώντας την κατηγορία wrapper Διπλό. Και δεύτερον, η απόδοση του Kotlin χρησιμοποιεί DoubleArray είναι συγκρίσιμη με - και σε αυτό το παράδειγμα ελαφρώς καλύτερη από - την απόδοση Java χρησιμοποιώντας τον πρωτόγονο τύπο διπλό.

Είναι σαφές ότι ο Kotlin έχει καταφέρει να βελτιστοποιήσει την ανάγκη για χωριστά συστήματα τύπου - με εξαίρεση την ανάγκη χρήσης τάξεων όπως DoubleArray αντί Πίνακας.

Συγκριτική αξιολόγηση 2: SciMark 2.0

Το άρθρο μου σχετικά με τα πρωτόγονα περιελάμβανε επίσης ένα δεύτερο, πιο επιστημονικό σημείο αναφοράς γνωστό ως SciMark 2.0, το οποίο αποτελεί σημείο αναφοράς για την επιστημονική και αριθμητική πληροφορική που διατίθεται από το Εθνικό Ινστιτούτο Προτύπων και Τεχνολογίας (NIST). Το σημείο αναφοράς SciMark μετρά την απόδοση πολλών υπολογιστικών ρουτίνων και αναφέρει μια σύνθετη βαθμολογία κατά προσέγγιση Mflops (εκατομμύρια λειτουργίες κινητής υποδιαστολής ανά δευτερόλεπτο). Έτσι, μεγαλύτεροι αριθμοί είναι καλύτεροι για αυτό το σημείο αναφοράς.

Με τη βοήθεια του IntelliJ IDEA, μετέτρεψα την έκδοση Java του δείκτη αναφοράς SciMark σε Kotlin. Το IntelliJ IDEA μετατρέπεται αυτόματα διπλό[] και int [] σε Java έως DoubleArray και IntArray στο Κότλιν. Στη συνέχεια συνέκρινα την έκδοση Java χρησιμοποιώντας πρωτόγονα με την έκδοση Kotlin χρησιμοποιώντας DoubleArray και IntArray. Όπως και πριν, έτρεξα και τις δύο εκδόσεις τρεις φορές και κατά μέσο όρο τα αποτελέσματα, τα οποία συνοψίζονται στον Πίνακα 2. Για άλλη μια φορά, ο πίνακας δείχνει περίπου συγκρίσιμα αποτελέσματα.

Πίνακας 2. Απόδοση χρόνου εκτέλεσης του δείκτη αναφοράς SciMark

Απόδοση (σε Mflops)
ΙάβαΚότλιν
1818.221815.78