Oficiálně vydán v roce 2016, Kotlin v posledních letech přitahoval velkou pozornost, zejména proto, že Google oznámil podporu Kotlin jako alternativu k Javě na platformách Android. S nedávno oznámeným rozhodnutím učinit Kotlin preferovaným jazykem pro Android si možná budete klást otázku, jestli je čas začít se učit nový programovací jazyk. Pokud je to váš případ, může vám tento článek pomoci rozhodnout se.
Historie vydání Kotlina
Kotlin byl oznámen v roce 2011, ale první stabilní vydání, verze 1.0, se objevilo až v roce 2016. Tento jazyk je bezplatný a otevřený, vyvinutý společností JetBrains a jejím hlavním vedoucím návrhářem jazyků je Andrey Breslav. Kotlin 1.3.40 byl vydán v červnu 2019.
O Kotlinovi
Kotlin je moderní, staticky napsaný programovací jazyk, který obsahuje jak objektově orientované, tak funkční programovací konstrukce. Zaměřuje se na několik platforem, včetně JVM, a je plně interoperabilní s Javou. V mnoha ohledech je Kotlin tím, jak by Java mohla vypadat, kdyby byla navržena dnes. V tomto článku představuji osm funkcí Kotlin, které, jak věřím, vývojáři Java budou nadšeni objevovat.
- Čistá a kompaktní syntaxe
- Systém jednoho typu (téměř)
- Nulová bezpečnost
- Funkce a funkční programování
- Datové třídy
- Rozšíření
- Přetížení obsluhy
- Objekty nejvyšší úrovně a vzor Singleton
Ahoj světe! Kotlin versus Java
Výpis 1 zobrazuje povinné „Ahoj, světe!“ funkce napsaná v Kotlin.
Výpis 1. „Ahoj, svět!“ v Kotlinu
fun main () {println ("Hello, world!")}
Jakkoli je tento příklad jednoduchý, odhaluje klíčové rozdíly od Javy.
hlavní
je funkce nejvyšší úrovně; to znamená, že funkce Kotlin nemusí být vnořeny do třídy.- Nejsou k dispozici žádné
veřejná statika
modifikátory. Zatímco Kotlin má modifikátory viditelnosti, výchozí jeveřejnost
a lze jej vynechat. Kotlin také nepodporujestatický
modifikátor, ale v tomto případě to není nutné, protožehlavní
je funkce nejvyšší úrovně. - Od verze Kotlin 1.3 je parametr pole řetězců pro
hlavní
není vyžadováno a pokud není použito, může být vynecháno. V případě potřeby by to bylo deklarováno jakoargs: Pole
. - Pro funkci není zadán žádný návratový typ. Kde Java používá
prázdnota
, Kotlin používáJednotka
, a pokud je návratový typ funkceJednotka
, může být vynechán. - V této funkci nejsou žádné středníky. V Kotlinu jsou středníky volitelné, a proto jsou zalomení řádků významné.
To je přehled, ale je toho mnohem víc, co se dozvíte o tom, jak se Kotlin liší od Javy a v mnoha případech ji vylepšuje.
1. Čistší a kompaktnější syntaxe
Java je často kritizována za to, že je příliš podrobná, ale některá výřečnost může být vaším přítelem, zvláště pokud je zdrojový kód srozumitelnější. Výzvou v jazykovém designu je snížit výřečnost při zachování jasnosti a myslím si, že Kotlin jde dlouhou cestou ke splnění této výzvy.
Jak jste viděli v seznamu 1, Kotlin nevyžaduje středníky a umožňuje vynechat návratový typ pro Jednotka
funkce. Zvažme několik dalších funkcí, díky nimž je Kotlin čistší a kompaktnější alternativou k Javě.
Odvození typu
V Kotlin můžete deklarovat proměnnou jako var x: Int = 5
, nebo můžete použít kratší, ale stejně jasnou verzi var x = 5
. (Zatímco Java nyní podporuje var
tato funkce se objevila až v Javě 10, dlouho poté, co se funkce objevila v Kotlin.)
Kotlin také má val
deklarace pro proměnné jen pro čtení, které jsou analogické s proměnnými Javy, které byly deklarovány jako finále
, což znamená, že proměnnou nelze přiřadit. Výpis 2 uvádí příklad.
Výpis 2. Proměnné jen pro čtení v Kotlin
val x = 5 ... x = 6 // CHYBA: NEZPRACUJE se
Vlastnosti versus pole
Tam, kde má Java pole, má Kotlin vlastnosti. Vlastnosti jsou deklarovány a je k nim přistupováno podobným způsobem jako k veřejným polím v Javě, ale Kotlin poskytuje výchozí implementace funkcí accessor / mutator pro vlastnosti; to znamená, že Kotlin poskytuje dostat()
funkce pro val
vlastnosti a obojí dostat()
a soubor()
funkce pro var
vlastnosti. Přizpůsobené verze dostat()
a soubor()
lze v případě potřeby implementovat.
Většina vlastností v Kotlin bude mít záložní pole, ale je možné definovat a vypočítaná vlastnost, což je v zásadě a dostat()
funkce bez doprovodného pole. Například třída představující osobu může mít vlastnost pro datum narození
a vypočítaná vlastnost pro stáří
.
Výchozí a explicitní importy
Java implicitně importuje třídy definované v balíčku java.lang
, ale všechny ostatní třídy musí být explicitně importovány. Ve výsledku mnoho zdrojových souborů Java začíná importem tříd kolekce z java.util
, I / O třídy z java.io
, a tak dále. Ve výchozím nastavení Kotlin implicitně importuje kotlin. *
, což je zhruba analogické s importem Java java.lang. *
, ale Kotlin také dováží kotlin.io. *
, kotlin.collections. *
a třídy z několika dalších balíčků. Z tohoto důvodu zdrojové soubory Kotlin obvykle vyžadují méně explicitních importů než zdrojové soubory Java, zejména pro třídy, které používají kolekce nebo standardní I / O.
Žádné volání „nového“ pro konstruktéry
V Kotlin, klíčové slovo Nový
není nutné k vytvoření nového objektu. Chcete-li volat konstruktor, stačí použít název třídy se závorkami. Kód Java
Student s = nový Student (...); // nebo var s = new Student (...);
lze v Kotlinu napsat takto:
var s = Student (...)
Řetězcové šablony
Řetězce mohou obsahovat výrazy šablony, což jsou výrazy, které jsou vyhodnoceny s výsledky vloženými do řetězce. Výraz šablony začíná znakem dolaru ($) a skládá se buď z jednoduchého názvu, nebo z libovolného výrazu ve složených závorkách. Řetězcové šablony mohou zkrátit řetězcové výrazy snížením potřeby explicitního zřetězení řetězců. Jako příklad následující kód Java
println ("Jméno:" + jméno + ", Oddělení:" + odd.);
lze nahradit kratším, ale ekvivalentním Kotlinovým kódem.
println ("Jméno: $ name, Oddělení: $ odd")
Rozšiřuje a implementuje
Programátoři Java vědí, že třída může rozšířit
další třída a nářadí
jedno nebo více rozhraní. V Kotlinu neexistuje žádný syntaktický rozdíl mezi těmito dvěma podobnými koncepty; Kotlin používá dvojtečku pro oba. Například kód Java
veřejná třída Student rozšiřuje Person implementuje Srovnatelné
by bylo napsáno jednodušeji v Kotlin takto:
třída Student: Osoba, srovnatelná
Žádné zaškrtnuté výjimky
Kotlin podporuje výjimky podobným způsobem jako Java s jedním velkým rozdílem - Kotlin nemá zaškrtnuté výjimky. I když byly dobře zamýšleny, kontrolované výjimky Java byly široce kritizovány. Stále můžeš házet
a chytit
výjimky, ale kompilátor Kotlin vás nenutí chytit žádnou z nich.
Destrukturalizace
Myslet na ničení jako jednoduchý způsob rozbití objektu na jeho jednotlivé části. Deklarace destrukce vytvoří více proměnných najednou. Výpis 3 níže poskytuje několik příkladů. U prvního příkladu předpokládejme tuto proměnnou student
je instance třídy Student
, který je definován v seznamu 12 níže. Druhý příklad je převzat přímo z dokumentace Kotlin.
Výpis 3. Příklady restrukturalizace
val (_, lName, fName) = student // extrahovat jméno a příjmení ze studentského objektu // podtržítko znamená, že nepotřebujeme student.id pro ((klíč, hodnota) v mapě) {// něco udělat s klíčem a hodnota}
výroky a výrazy „pokud“
V Kotlin, -li
lze použít pro tok řízení jako v Javě, ale lze jej také použít jako výraz. Tajemný ternární operátor Java (?:
) je nahrazen jasnějším, ale o něco delším -li
výraz. Například kód Java
dvojnásobek max = x> = y? x: y
bude napsáno v Kotlin takto:
val max = if (x> = y), pak x else y
Kotlin je v tomto případě o něco podrobnější než Java, ale syntaxe je pravděpodobně čitelnější.
'když' nahradí 'spínač'
Moje nejméně oblíbená kontrolní struktura v jazycích podobných C je přepínač
prohlášení. Kotlin nahrazuje přepínač
prohlášení s a když
prohlášení. Výpis 4 je převzat přímo z dokumentace Kotlin. Všimněte si toho přestávka
příkazy nejsou povinné a můžete snadno zahrnout rozsahy hodnot.
Výpis 4. Prohlášení „když“ v Kotlinu
when (x) {in 1..10 -> print ("x is in the range") in validNumbers -> print ("x is valid")! in 10..20 -> print ("x is outside the range ") else -> print (" nic z výše uvedeného ")}
Zkuste přepsat výpis 4 jako tradiční C / Java přepínač
prohlášení a získáte představu o tom, jak je nám u Kotlinových lépe když
prohlášení. Podobně jako -li
, když
lze použít jako výraz. V takovém případě se hodnota spokojené větve stane hodnotou celkového výrazu.
Přepínání výrazů v Javě
Java 12 představila výrazy přepínačů. Podobně jako Kotlin když
„Přepínací výrazy Java nevyžadují přestávka
výroky a lze je použít jako výroky nebo výrazy. Další informace o výrazech přepínačů v Javě najdete v části „Smyčka, přepnutí nebo pauza? Rozhodování a iterace s příkazy“.
2. Systém jednoho typu (téměř)
Java má dva samostatné systémy typů, primitivní typy a referenční typy (neboli objekty). Existuje mnoho důvodů, proč Java obsahuje dva samostatné typy systémů. Ve skutečnosti to není pravda. Jak je uvedeno v mém článku Případ pro udržování primitiv v Javě, pro primitivní typy existuje opravdu jen jeden důvod - výkon. Podobně jako Scala, Kotlin má pouze jeden typový systém v tom, že v zásadě neexistuje žádný rozdíl mezi primitivními typy a referenčními typy v Kotlin. Kotlin používá primitivní typy, pokud je to možné, ale v případě potřeby použije objekty.
Proč tedy námitka „téměř“? Protože Kotlin má také specializované třídy, které představují pole primitivních typů bez režie autoboxingu: IntArray
, DoubleArray
, a tak dále. Na JVM DoubleArray
je implementován jako dvojnásobek[]
. Používá DoubleArray
opravdu změnit? Uvidíme.
Benchmark 1: Násobení matic
Při vytváření případu pro primitivy Java jsem ukázal několik srovnávacích výsledků porovnávajících primitivy Java, třídy obálky Java a podobný kód v jiných jazycích. Jedním z měřítek bylo jednoduché násobení matic. Pro porovnání výkonu Kotlin s Javou jsem vytvořil dvě implementace násobení matic pro Kotlin, jednu s použitím Pole
a jeden pomocí Pole
. Výpis 5 ukazuje implementaci Kotlin pomocí Pole
.
Výpis 5. Násobení matic v Kotlin
fun multiply (a: Array, b: Array): Array {if (! checkArgs (a, b)) throw Exception ("Matrix are not compatible for multiplication") val nRows = a.size val nCols = b [0]. size val result = Array (nRows, {_ -> DoubleArray (nCols, {_ -> 0.0})}) for (rowNum in 0 until nRows) {for (colNum in 0 until nCols) {var sum = 0.0 for (i v 0 do a [0] .size) sum + = a [rowNum] [i] * b [i] [colNum] výsledek [rowNum] [colNum] = sum}} návratový výsledek}
Dále jsem porovnal výkon dvou verzí Kotlin s výkonem Java dvojnásobek
a Java s Dvojnásobek
, běží všechny čtyři měřítka na mém aktuálním notebooku. Vzhledem k tomu, že při spouštění každého benchmarku existuje malé množství „šumu“, spustil jsem všechny verze třikrát a zprůměroval jsem výsledky, které jsou shrnuty v tabulce 1.
Tabulka 1. Runtime výkon maticového násobení
Jáva ( | Jáva ( | Kotlin ( | Kotlin ( |
7.30 | 29.83 | 6.81 | 15.82 |
Tyto výsledky mě poněkud překvapily a nakreslil jsem dvě jídla. Nejprve výkon Kotlin pomocí DoubleArray
je jasně lepší než výkon Kotlin Pole
, což je zjevně lepší než u Javy pomocí třídy wrapper Dvojnásobek
. A za druhé, výkon Kotlin pomocí DoubleArray
je srovnatelný - a v tomto příkladě o něco lepší než - výkon Java pomocí primitivního typu dvojnásobek
.
Je zřejmé, že Kotlin odvedl skvělou práci při optimalizaci potřeby samostatných typů systémů - s výjimkou nutnosti používat třídy jako DoubleArray
namísto Pole
.
Benchmark 2: SciMark 2.0
Můj článek o primitivech také zahrnoval druhý, více vědecký benchmark známý jako SciMark 2.0, což je Java benchmark pro vědecké a numerické výpočty dostupný od National Institute of Standards and Technology (NIST). Benchmark SciMark měří výkon několika výpočetních rutin a přibližně uvádí složené skóre Mflops (miliony operací s plovoucí desetinnou čárkou za sekundu). Větší čísla jsou tedy pro tuto referenční hodnotu lepší.
S pomocí IntelliJ IDEA jsem převedl Java verzi benchmarku SciMark na Kotlin. IntelliJ IDEA se automaticky převede dvojnásobek[]
a int []
v Javě do DoubleArray
a IntArray
v Kotlinu. Pak jsem porovnal verzi Java pomocí primitiv s verzí Kotlin pomocí DoubleArray
a IntArray
. Stejně jako dříve jsem obě verze běžel třikrát a zprůměroval jsem výsledky, které jsou shrnuty v tabulce 2. Tabulka opět ukazuje zhruba srovnatelné výsledky.
Tabulka 2. Výkon za běhu benchmarku SciMark
Jáva | Kotlin |
1818.22 | 1815.78 |