Uz od javy 8 slysim, jak je GC uzasny a vylepseny a s kazdou dalsi verzi prichazi novy a novy, jeste lepsi a belejsi, nez ten predtim.
Ma vize je, ze java zacne pomalu zanikat. Pro google je jeji implementace v androide nechtene dite, na serveru je cim dal tim min potreba....
Bohuzel to bude pomale odumirani....
Já bych na to vůbec nevsázel, myslím, že Java tu bude ještě dlouho po té, co Kotlin zůstane kuriozitou pro pár fandů. Kotlin bych bral spíš jako prototyp, otestují se na něm nějaké věci, a pak přijde něco dalšího, kde budou převzaté věci z Kotlinu, které se osvědčily, a zapomene se na ty, které se nepovedly. Na Kotlinu je až příliš vidět, že je to jazyk psaný odspoda, programátory – chybí tam akademický nadhled, nějaký přesah. Kotlin vypadá, jako když pejsek a kočička vařili dort – aby byl dort dobrý, dali do něj všechno dobré. Akorát ty dílčí dobré věci nefungují spolu navzájem.
Cakali sme na prveho dementa , ktory v java vlakne napise nieco podobne. Blahozelam , ziskavas bobrika kratkozrakosti. Pre skusenych java backend vyvojarov , vyuzivajucich naplno vyhody serverovych frameworkov nie je nic horsie ako prebrat kod po developeroch , ktori si vsetko predsa napisu sami ...
. (neregistrovaný), 11:40: Porovnávat jazyk a součásti standardní běhové knihovny je jako porovnávat hrušky s přepravkou. Java má od verze 6 podporu skriptovacích jazyků (JSR-223), přímo součástí standardní knihovny JRE byl interpret JavaScriptu Rhino, který byl v JRE 8 nahrazen interpretrem Nashorn. Pokud dál chcete používat JavaScript pomocí JSR-223, budete muset k aplikaci přibalit interpret jako knihovnu, to je vše. Ale pokud to s JavaScriptem v kombinaci s Javou myslíte vážně, podívejte se raději na GraalVM, protože to je budoucnost kombinace Java+JavaScript (a dalších jazyků). Nedává moc smysl udržovat interpret JavaScriptu jako součást standardní knihovny, když máte podporu JavaScriptu zabudovanou přímo v JVM.
Například data classes nemohou být open (i když od verze 1.1 aspoň mohou být samy zděděné). Chápu proč to tak je, ale je to přesně ten přístup „přidáme dobrou věc, a už neřešíme, jak spolupracuje se ostatními dobrými věcmi“. Nebo při inicializaci property v primárním konstruktoru nejde použít delegated properties. Opět – inicializace property v primárním konstruktoru je pěkný syntaktický cukřík, delegované properties také. Ale neřeší se, co se stane, když to někdo bude chtít použít dohromady. Vlastně by to jít použít mělo, v dokumentaci jsem nenašel nic o tom, že to nejde, ale kompilátor to prostě nepřeloží, je to neznámá syntaxe. Víc mne z hlavy nenapadá, narazil jsem ještě asi na jeden nebo dva případy během snahy napsat reálný kód. Pak jsem si ještě procházel dokumentaci, a tam přímo bilo do očí, jak každá ta jednotlivá vlastnost je popsaná samostatně s několikařádkovým příkladem, kde je použitá přesně ta jediná vlastnost (pokud možno porovnaná s Javou), ale nikde tam nejsou větší příklady, kde by to bylo zkombinované. Nemůžu si pomoci, ale na mne z toho čiší postup vzniku „hm, tohle mne v Javě nebaví, to by šlo udělat lépe, přidáme na tuhle specialitu novou vlastnost do jazyka“.
Nápady jsou to často hezké, Kotlin se nepochybně nějakou dobu používat bude a určitě bude inspirací pro další jazyky, ale je až příliš viditelné, že je to Java se spoustou záplat. Navíc nemám rád jazyky, které mají tak košatou syntaxi a kdejaké zjednodušení se řeší přímo na úrovni jazyka. (Pro mne už je mírně za hranou Groovy, a Kotlin jde ještě podstatně dál.) Javě se vytýká, že je ukecaná, protože samotný jazyk je vlastně velmi jednoduchý a spousta věcí se řeší až v runtime knihovně. Jenže ono to má tu výhodu, že se pak knihovna může měnit a vyvíjet, nové konstrukce si může přidávat vlastně kdokoli, a jazyk je pořád stejný. Třeba když porovnám null safety, Kotlin si vystačí s jedním až dvěma znaky, Java má Optional<>
. Jenže Optional<>
mohl do Javy zavést kdokoli, stačilo napsat normální knihovnu (a různé knihovny podobnou funkcionalitu měly). Když se v budoucnosti ukáže, že by se to dalo řešit lépe, vymyslí někdo třeba inverzní třídu Mandatory<>
, a na syntaxi Javy se nemusí měnit nic. V Kotlinu budou shánět další symbol – a hlavně takovou novinku mohou zavést jenom autoři Kotlinu, nikdo jiný.
Zrovna Optional je trochu nešťastný příklad:
* Může to teoreticky implementovat kdokoliv, ale úspěch zde dost závisí na tom, aby se to široce používalo. Ve Scale se to s podobným typem Option podařilo, v Javě zatím ne. Kdyby to někdo implementoval jako 3rd party knihovnu, šance na přijetí by byly IMHO celkem mizivé.
* V jazyce bez hodnoty null by to byla fajn věc, v Javě mám ve výsledku proměnnou, která kromě Optional.of(value) může obsahovat nejen Optional.empty(), ale také null. (Ve Scale je to ještě o kousek dál: Some(value), None, null a navíc Some(null).) V tomto směru je podpora přímo v jazyce elegantnější, nenabízí žádné podivné hodnoty, které nechceme.
Tím nepopírám výhody implementace fíčur jako knihovny (ostatně Scala je na tomto principu postavená), čistě jen vyzdvihuju nevhodně zvolený příklad.
Šlo jen o porovnání toho, že je nějaká vlastnost implementována jako běžná třída, nebo že je implementována jako speciální operátory. Ostatně Kotlin má úplně stejný problém, pokud se v Kotlinovém kódu bude používat spousta Java knihoven, ze kterých Kotlin nebude schopen zjistit nullabilitu, bude jeho null safety také neúspěšné. Dichotomie mezi null a Optional.empty() je daná prostě tím, že Optional bylo zavedeno dodatečně.
Zkuste si to představit tak, že byste vytvářel nový jazyk, a rozhodoval se, zda null safety implementujete jako třídu Optional nebo jako „operátor“ otazník.
Jsou pěkné argumenty pro Optional, ve Scale to ostatně celkem dobře funguje (dokud se nedotknu Javové knihovny s null), protože se tam null prakticky nepoužívá. Scala se z Javových API snaží napřímo používat co možná nejméně, spíše přes nějaký wrapper. Ve výsledku máme i celkem dobrou iluzi, že null neexistuje, a Option může dobře fungovat… Je to ale zásluha celého ekosystému, ne jen návrhu samotného typu v Javě.
V konzervativnější Javě s dekádami dědictví to ale s přechodem na Optional tak dobré už není. Máme – i v JRE – spoustu tříd, které prostě používají null, a nelze to jen tak změnit kvůli zpětné kompatibilitě.
U Kotlinu můžeme mít horší nebo lepší podporu v závislosti na tom, jestli jsou parametry a návratové hodnoty označeny anotacemi jako @Nullable a @NonNull (či podobnými). I bez těchto anotací by IMHO mělo jít využít některé výhody nových operátorů (např. Elvis operator), jen prostě kompilátor nebude tak přesně varovat. Navíc ten kód lze průběžně o anotace obohacovat, aniž bych rozbíjel kompatibilitu. To se o Optional říct nedá.
> Zkuste si to představit tak, že byste vytvářel nový jazyk, a rozhodoval se, zda null safety implementujete jako třídu Optional nebo jako „operátor“ otazník.
To bych asi rozhodl podle cílů, jaké by ten jazyk měl. Více se mi líbí Optional, ale pokud bych měl dobře zapadnout do existujícího ekosystému (což tvůrci Kotlinu AFAIR psali jako jeden z cílů), asi by se to nějak promítlo i do jazyka. Možná bych vymyslel nějaké lepší řešení, ale těžko by se to obešlo bez nějaké speciální vlastnosti jazyka.
To bych asi rozhodl podle cílů, jaké by ten jazyk měl. Více se mi líbí Optional, ale pokud bych měl dobře zapadnout do existujícího ekosystému (což tvůrci Kotlinu AFAIR psali jako jeden z cílů), asi by se to nějak promítlo i do jazyka. Možná bych vymyslel nějaké lepší řešení, ale těžko by se to obešlo bez nějaké speciální vlastnosti jazyka.
A to je právě to, co se mi na Kotlinu nelíbí. Řeší aktuální problém, tak přidají novou konstrukci do jazyka. Jenže za dvacet let tady ten problém už dávno nebude, ale konstrukce v jazyku zůstane pořád. V Javě do stejné kategorie patří primitivní typy, ale nic moc dalšího mne nenapadá. A i ty mají nakonec aspoň tu výhodu, že jsou not-null.
Upravený příklad z dokumentace by vypadal takto (nepovedlo se mi přijít na to, jak tu nějak rozumně zprovoznit formátování):
import kotlin.properties.Delegates
class User(name: String){
var name: String by Delegates.observable(name) {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User("initial value")
user.name = "first"
user.name = "second"
}
Zaprvé, celý syntax je val/var <property name>: <Type> by <expression>
, nikde u toho není nastavování default value, protože někdo může chtít inicializovat delegated property přes její konstruktor (viz výše) anebo přes setter (na to jde použít init block). Nehledě na to, že delegated properties můžou být read-only, takže tam by ani inicializace přes setter nedávala smysl.
> když porovnám null safety, Kotlin si vystačí s jedním až dvěma znaky, Java má Optional<>
. Jenže Optional<>
mohl do Javy zavést kdokoli, stačilo napsat normální knihovnu (a různé knihovny podobnou funkcionalitu měly). Když se v budoucnosti ukáže, že by se to dalo řešit lépe, vymyslí někdo třeba inverzní třídu Mandatory<>
, a na syntaxi Javy se nemusí měnit nic. Kotlinu budou shánět další symbol – a hlavně takovou novinku mohou zavést jenom autoři Kotlinu, nikdo jiný.
V Javě pak díky zpětné kompabilitě zůstane null, Optional<>
a Mandatory<>
. S tím se bude určitě dobře pracovat. Navíc v Kotlinu se mohou použít takové věci jako autocasting nebo safe calls, toho žádná knihovna v Javě docílit nemůže, nemluvě o runtime overheadu pro Optional<>
.
Upravený příklad z dokumentace by vypadal takto
Jenže v tomhle příkladu právě delegát není definován v konstruktoru. Ta celá syntaxe s klíčovým slovem by
při deklaraci property v konstruktoru nejde použít, takže jsou v jazyce dvě velmi podobné konstrukce – deklarace property v těle třídy a deklarace v konstruktoru. Ve spoustě případů je dokonce syntaxe stejná.
V Javě pak díky zpětné kompabilitě zůstane null, Optional<> a Mandatory<>. S tím se bude určitě dobře pracovat.
Už jsem vysvětloval, že to porovnáváte nesouvisející věci. Popisujete zpětnou kompatibilitu a porovnáváte jí s novým jazykem. Já jsem ale psal o rozdílu implementace ve standardní knihovně vs. nová konstrukce v jazyce.
safe calls, toho žádná knihovna v Javě docílit nemůže
Ve skutečnosti safe calls s Optional<>
můžete použít a je to implementováno pouze ve standardní knihovně, není potřeba žádná magie kompilátoru.
nemluvě o runtime overheadu pro Optional<>
To je pouze otázka implementace – když je to součástí standardní knihovny, nic nebrání kompilátoru nebo JVM dělat nad tou třídou nějakou „magii“. Třeba java.lang.String
se „z venku“ také tváří jako úplně normální třída, ale kompilátor s ní občas zachází speciálním způsobem.
Asi vsichni tady vime, ze kdyby jsi psal GC ty, tak ho napises nejlepsi hned v prvni verzi a dal nepujde vubec optimalizovat bez ohledu na novy HW, SW atd. Soudruzi co pisou GC v jave nejsou tak uzasni a nemaji tolik vizionaru jako jsi ty, ktery umi predpovedet budoucnost optimalizaci ze sve ranni stolice. Treba by te zamestnali...
To je proto, že garbage collection je těžký úkol. Přesněji řečeno udělat garbage collector, který funguje vždy, rychle a účinně (ve smyslu toho, že hodně čistí paměť) je obecně nemožné, je to algoritmicky neřešitelný úkol. Proto GC používají různé chytristiky a heuristiky a ty je možné vylepšovat v podstatě donekonečna. Takže GC budou pořád lepší a lepší, ale nikdy nebudou "dokonalé" nebo "hotové". Kdyby GC byl jednoduchý problém, byl by už dávno vyřešený a nebylo by se o čem bavit.
Jestli by nebylo lepší nepoužívat GC vůbec, jako to mají už vyřešené v jazyce Rust.
https://doc.rust-lang.org/book/2018-edition/ch04-01-what-is-ownership.html
Se stack alokací popsanou v článku si většina programů nevystačí. A pokud nalistuješ v té knize o pár kapitol dál, tak zjistíš, že Rust používá pro sdílené vlastnictví smart pointery (což je automatická správa paměti). Navíc na rozdíl od javovského GC, přestanou smart pointery fungovat pokud vytvoříš cyklus a je na zodpovědnosti programátora, aby se to nestalo. Takže i autoři jazyků jako C++ nebo Rust si uvědomují, že manuální správa paměti je těžký úkol, který ve větším systému nikdo nezvládne dokonale a snaží se jim to ulehčovat i za cenu paměťových a výpočetních nároků (počítadlo referencí - alokace, dekrementace, inkrementace).
Pracuju jako C++ programátor pár let a nepracoval jsem s týmem, který by preferoval manuální správu paměti před smart pointery. Naopak obyčejné pointery jsou věc, na kterou se hned reaguje v code review a musíš si obhájit, proč použít zrovna je. Vsadím se, že doporučení pro rust budou podobná.
Rust nerozlišuje mezi stack a heap alokací, v obou případech platí stejná pravidla pro vlastnictví objektu. Automatická správa paměti je v Rustu jak pro normální reference, tak pro smart pointery. Zjednodušeně řečeno vždy platí, že objekt může mít jen jednoho vlastníka pro zápis, nebo nekonečně mnoho vlastníků pro čtení.
Smart pointery v Rustu mají nějaké featury navíc oproti normálním referencím, nicméně je to něco za něco. Např. RefCell<T> řeší vlastnictví objektu až v runtime, takže to nejde kontrolovat při překladu. Pokud napíšeš v Rustu program, který porušuje pravidla vlastnictví, tak s referencemi případně s Box<T> se to vůbec nepřeloží. S RefCell<T> se to přeloží, ale spadne to až v runtime ve chvíli, kdy použiješ RefCell<T> špatně a pravidla vlastnictví porušíš.
sak mame Epsilon ne - http://openjdk.java.net/jeps/318 :)
ano, to byl plně stačilo ... https://googleprojectzero.blogspot.com/2018/09/a-cache-invalidation-bug-in-linux.html