Tak zrovna ta typová inference je prakticky k ničemu. Pokud jsem zvyklý deklarovat přímo tak proč to měnit, že? Nejspíš ústupek JavaScriptkákům aby snaze přešli na Javu. Další JEP jsou opět jenom lokální a málo využitelné funkce. Jinak příchod nové verze co 1/2 roku to není dobrý nápad. např. NetBeans ještě stále nemá podporu pro Java9, kromě nighty build ale už mrsknou ven Java10.
Pokud mám vhodné IDE, tak informaci o typu budu mít, kdykoli budu chtít. Funguje to tk u Scaly a nevidím důvod, proč ne i u Javy.
Při psaní kódu to ušetří trošku práce a i trošku rozptýlení od problému, který řeším. Při čtení je to jak kdy – pokud nepotřebuju vědět typ (docela často), jeho vynechání mi čtení kódu spíš urychluje, nemusím přeskakovat text proměnlivé délky, abych se dostal k názvu proměnné. Zvlášť v případě složitějších typů jako Map<String, Function<Int, Set<String>> je to rozdíl. Pokud chci vědět typ, musím v IDE udělat krok navíc. A není to přívětivé k lidem, kteří o takovéto možnosti nevědí.
Ideálňí by bylo, kdyby to IDE umělo zobrazovat vždy, ale nějak graficky odděleně.
U Scaly to funguje, protoze tam je to typove mnohem silnejsi a mnozstvi nedefinovanych prirazeni v Jave v Scale jdou bez problemu; v Jave to dopadne jako u Pythonu, kde clovek musi jit na to debugrem, aby zjistil, co to tam vlastne ma za typ. Nebo jako u C++ s auto, coz je uplne priserny a bez debugeru nic nejde. Zkuste si udelat auto promennou v C++ z naky knihovny, co vraci x-krat vnorenou template, a pak na to napsat funkci, co to bude chtit jako parametr... Bez debugru nenapisete ani header te funkce.
U Scaly to s vhodným IDE (např. IDEA) funguje tak, že když nevím, mohu použít Alt+equals nebo stisknout Ctrl a najet myší. Není potřeba to spouštět a brát na to debugger.
IDEA Ctrl a najetí myší podporuje dnes i u Javy. Zkratka Alt+equals je zatím specifická pro Scalu, ale to se může snad celkem snadno změnit…
Idea umí v kódu zobrazit odvozené nebo externí kontraktové anotace, umí v kódu zobrazit jména parametrů volaných funkcí, takže určitě brzy bude umět i zobrazit přímo v kódu odvozené typy (jsem zvědav, jak se poperou se zobrazováním typů, které nejde v Java syntaxi zapsat). A naopak už před příchodem odvozených generických typů uměla opakující se definici na pravé straně zobrazit jako <~>
nebo umí i v Java 6 a Java 7 zdrojácích zobrazit anonymní vnitřní třídu jako lambdu. A deklaraci proměnné vytvořím pomocí postfixu .var
a doplnění kódu. Takže typová inference bude sloužit hlavně těm, kteří používají hloupá IDE nebo jen textové editory, a pak pro těch několik případů, kdy není možné typ současnými výrazovými prostředky Javy vyjádřit.
Ano, IDEA je někdy trochu výlet do budoucnosti, i když to není dokonalé. Třeba ty typy by v jednodušších případech bylo přehlednější defaultně nevidět, nebo ještě lépe vidět až kus mimo. To – co vím – zatím neumí.
Postfix .var jsem neznal, zatím jsem používal spíš označení výrazu (ctrl+W) a extrakci jako proměnnou (asi ctrl+alt+V*). To mi (možná díky nastavení) defaultně přidává i final. (I když, pokud neexistuje .val, nemělo by být tak těžké ho přidat.)
K případům, které není možné dnes v Javě vyjádřit – k tomu jsem skeptický, pokud něco nebude umět Java vyjádřit, pak se to dost možná nedostane ani do inferovaného typu. Možná bude výjimka new Object(){public method…}, kde AFAIR lze tu metodu dnes zavolat okamžitě, a tam to teoreticky asi půjde použít i po uložení do lokální proměnné. Praktický význam bude ale asi minimální.
Nebo máte nějaký jiný příklad?
*) Popravdě u klávesových zkratek často nevím, jak přesně jsou, mám spíš naučený pohyb na klávesnici.
1. OK, i když v praxi jsem to moc nepotřeboval. A kde jo, tam by toto stejně asi nestačilo, protože ten typ by se musel třeba objevit ve fieldu.
2. Doslova vzato, to „= 0“ může být i v inicializaci. Ale ano, může tu být třeba nějaká další událost. To ale zavání: Pokud to chcete v jedné metodě, nejspíš dost naroste nad doporučované/uznávané meze. Pokud to dáte mimo, tak to jednak použít nemůžete a jednak ten field můžete dát taky mimo.
Tzn. uplatnění to mít může (i když asi ne moc časté), podmínkou ovšem je, že to bude podporované. O čemž v prvním případě trochu pochybuju – nezmění-li se možnosti JVM, nabízí se tři způsoby, jak to kompilovat, aby ten příklad fungoval:
a. Výsledek metody get přetypovat na nejbližšího společného předka (tady Object) nebo vůbec nepřetypovávat. Při skoro každém volání pak bude nutné přetypování pomocí instrukce checkcast. Což by mimochodem znamenalo, že v případě špatného typu by došlo k ClassCastException na místě, kde se tak tomu stát dosud nemohlo.
b. Vybrat jeden z typů a v případě potřeby přetypovat. Asi výkonnější, ale zmatenější alternativa k A, zejména v tom, kdy nastane případná ClassCastException. Toto IMHO neprojde.
c. Mít interně dvě proměnné, do každé to přetypovat pod jiným typem. Chování by bylo bližší současnému (k případné ClassCastException by mohlo dojít okamžitě po zavolání get), opět by to ale ovlivnilo další práci s proměnnou, zvlášť pokud by byla mutable.
Pak se nabízejí ještě dvě jiné možnosti: upravit možnosti bytecode (IMHO se nestane) nebo možnosti jazyka nějak omezit, aby to takto použít nešlo (na to bych se možňá i vsadil). Hádám, že se vám tam inferuje java.lang.Object (nebo obecně nejspecifičtější společný typ).
Druhý příklad asi fungovat bude, tam se na úrovni bytecode prostě vytvoří pojmenovaná třída (ve stylu Foo$1), jejíž název lze použít.
Vít Šesták: Nechápu, proč o tom píšete v budoucím čase a v podmiňovacím způsobu. Java 10 už je vydaná a to, co jsem napsal, použít můžete – jinak bych to sem nepsal. Zkuste si pod JDK 10 přeložit a spustit následující kód:
package test; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { var list = Main.createList(); var item = list.get(0); System.out.println(item); } private static <T extends Serializable & CharSequence> List<T> createList() { ArrayList<T> list = new ArrayList<>(); list.add((T) "0"); return list; } }
A nebo něco kratšího:
package test; import java.util.List; public class Main { public static void main(String[] args) { var list = List.of("0", new StringBuilder()); var item = list.get(0); System.out.println(item); } }
Na bajtkódu se nic nemění, odvozené typy lokálních proměnných jsou záležitost čistě syntaxe Javy a kompilátoru. Takže v bajtkódu bude přesně to samé, jako kdybyste do kódu nejpřesnější možný typ napsal sám, v tomto případě tedy Object
. Není to nic nového, generiky v Javě 5 byly také udělané tak, že se v bajtkódu nic nezměnilo a generické typy hlídal jen kompilátor.
Jak často se to bude používat nevím (ale tipl bych si, že např. díky List.of()
nebo Map.of()
docela často), ale je to jedno, protože kompilátor to musí umět, i kdyby se to používalo málo.
Aha, nevěděl jsem, že JDK 10 už je vydané.
Vím, jak fungují generika, včetně toho, jak se překládají do instrukcí bytecode. I proto jsem zvědav, jak se budou překládat ty intersection types. Možná se to bude nějak divoce castovat, možná se interně použijí dvě (nebo více) proměnné. To jsou zhruba ty návrhy, které jsem psal. Nebo nějak rozšířit možnosti bytecode. Až budu u počítače, asi si to zkusím disassemblovat.
Ony ty intersection types dříve na úrovni bytecode moc potřeba nebyly. Pokud to chcete použít hned, využijete jen jeden typ, pokud jste to chtěl uložit, musel jste jeden typ explicitně specifikovat. Možná se to ale vlastně muselo vyřešit v JDK 8 kvůli lambdě, kde je taky inference.
Tak jsem to vyzkoušel. Trefil jsem, že bytecode kvůli tomu měnit nebudou, dál už můj odhad moc dobrý nebyl. Překládá se to z hlediska konzistence chování tím nejšílenějším způsobem, který mě napadl, tedy uloží se to jako proměnná jednoho typu a na druhý typ se castuje v případě potřeby:
public static void main(java.lang.String[]); Code: 0: ldc #2 // String 0 2: new #3 // class java/lang/StringBuilder 5: dup 6: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 9: invokestatic #5 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List; 12: astore_1 13: aload_1 14: iconst_0 15: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 20: checkcast #7 // class java/io/Serializable 23: astore_2 24: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 27: aload_2 28: checkcast #9 // class java/lang/CharSequence 31: iconst_0 32: invokeinterface #10, 2 // InterfaceMethod java/lang/CharSequence.charAt:(I)C 37: invokevirtual #11 // Method java/io/PrintStream.println:(C)V 40: return
Kdybych to měl zpětně přeložit do Javy (bez generik), vypadalo by to asi takto:
public static void main(java.lang.String[] v0){ java.util.List v1 = java.util.List.of("0" , new java.lang.StringBuilder()); java.io.Serializable v2 = (java.io.Serializable)v1.get(0); java.lang.System.out.println(((java.lang.CharSequence)v2).charAt(0)); }
To ovšem vede k novým edge cases: https://gist.github.com/v6ak/794a6f81d5d06349dcc6eb8d741042b7
To, že generickou kolekci přetypujete na raw a uložíte do ní objekt, který by vám u generické kolekce kompilátor odmítl, není žádný edge case a už vůbec to nesouvisí s typovou inferencí. Tohle vám samozřejmě za běhu spadne, a to už od verze Java 1.5. Proto taky kompilátor při každém použití raw typů vypisuje varování…
List<Number> list = new ArrayList<>(); List raw = (List) list; raw.add("string"); Number number = list.get(0);
Já ale nepíšu o tom, že je možné do kolekce vecpat něco, co tam nepatří. Ano, to šlo odjakživa. Já píšu o tom, co to způsobí. Že dojde k chybě za běhu je jasné, je tu ale určitá nekonzistence, kdy k té chybě dojde. V jednom případě je to bezprostředně po zavolání List.get(T), ve druhém případě je to až před pokusem volat CharSequence.charAt(int). Přitom při pohledu na zdroják byste těžko hledal rozdíl, proč se to chová rozdílně, a osobně ze zdrojáku neumím odvodit, kdy dojde k jakému chování. Na to potřebuju disassembler (nebo experiment). Možná je to podle abecedního pořadí, těžko obecně říct.
K té chybě dojde v okamžiku pokusu o přetypování na typ, který není danou instancí implementován. V obou případech. To, že je to v mé ukázce bezprostředně po zavolaní list.get(0)
je dané jenom shodou okolností, totiž že to přetypování následuje bezprostředně po tomto volání.
List<Number> list = new ArrayList<>(); List raw = (List) list; raw.add("string"); Object item = list.get(0); ((Number) item).intValue();
V tom kódu je chyba v okamžiku vložení prvku určitého typu do kolekce, která daný typ nemůže obsahovat. Java kompilátor z důvodu zpětné kompatibility na této chybě neskončí, pouze vypíše varování o použití unchecked nebo unsafe operace. Že se ta chyba neprojeví hned, ale až někde později v kódu, to je celkem běžné.
Že se v některých případech pohledem do zdrojáku nepozná, co to bude dělat, a je nutné si to najít ve specifikaci (nebo spíš ten kód přepsat), to se stává. Java se tomu docela snaží bránit, ale i tam se okrajové případy najdou. To, co popisujete vy, není nic nového. Tady máte to samé v bledě modrém, řádek s value3
nepůjde zkompilovat ze stejného důvodu, z jakého ten váš kód vyhazuje výjimku – návratový typ getItem()
je Serializable
:
package test; import java.io.Serializable; public class Main<T extends Serializable & CharSequence> { public T getItem() { return null; } public static void main(String[] args) { CharSequence value1 = new Main<>().getItem(); Serializable value2 = new Main<>().getItem(); CharSequence value3 = new Main().getItem(); Serializable value4 = new Main().getItem(); } }
Akorát je na tomhle příkladu asi o něco zřetelnější, že pořadí není abecední, ale záleží na pořadí ve zdrojáku.
Díky za vysvětlení, jak fungují generika, ale to celkem vím, včetně type erasure, raw types a unsafe casts. To vytvoření Listu neberu jako způsob, jak se to má dělat (a navíc by to šlo i při stejné funkcionalitě napsat čistěji, je to PoC quality), ale je to situace, která může nějak omylem nastat. A jen jsem holt čekal, že se s tím javac popasuje trochu konzistentněji. Ono by to nebylo tak těžké – stačil by jeden checkcast navíc. Vliv na výkon by byl v nejhorším případě minimální, v některých případech by mohl být s vhodnou JIT i pozitivní, protože statická analýza by ukázala, že některé checkcasty projdou vždy.
Čistě to napsat nejde, protože je to postavené na tom, že použijete raw type, čímž obejdete kontroly kompilátoru. Na tom mém příkladu ze včerejška je vidět, že už od Javy 1.5 se kompilátor musí v některých případech rozhodnout, který z typů použije do bajtkódu, a ostatní případy generických typů řeší skrytým přetypováním. Takhle je to nadefinované, že generiky řeší a kontroluje kompilátor, raw typy si musíte za běhu ošetřit sám. Javac se takhle chová už od verze 1.5 a je konzistentní, že úplně stejně se chová i při inferenci typů lokálních proměnných. Nekonzistentní by naopak bylo teď najednou do kódu zkontrolovaného za překladu pomocí generik na „náhodně“ vybraná místa přidávat kontrolu za běhu.
> Čistě to napsat nejde
Já nepsal „čistě“, ale „čistěji“. Dalo by se to napsat přidávání zbytečných prvků a jejich následného mazání.
> Javac se takhle chová už od verze 1.5
Myslím, že v Javě 1.5 nevytvoříte takovou situaci. Ano, můžete do kolekce dát prvky, které tam nepatří, ale to není přímo to, o čem píšu.
Já nepsal „čistě“, ale „čistěji“.
Nebyl jsem si jist, jak to myslíte. Ale „čistěji“ mi připadá bezpředmětné – pořád je to chyba a pořád to způsobí varování kompilátoru. Pokud byste měl příklad bez unchecked/unsafe varování, bylo by to něco jiného, pak bych to považoval za skutečný problém.
Myslím, že v Javě 1.5 nevytvoříte takovou situaci.
A ten druhý příklad z mého komentáře včera ve 22:09 je co? Podle mne je to přesně to samé, co jste uváděl vy, akorát tam není to automatické odvození typů, takže je musíte uvést explicitně ve zdrojovém kódu. Ale když to budete psát a budete muset napsat typ té lokální proměnné, budete úplně ve stejné situaci, jako v tom vašem příkladu. Jediný rozdíl je v tom, že dříve jste ten typ musel odvodit vy, odvodil ho i kompilátor a zkontroloval, zda do vámi uvedeného typu může přiřadit, jinak vypsal chybu. V Javě 10 můžete pomocí var
určit, ať použije rovnou ten typ, který odvodil sám.
Delal jsem kdysi pro SUN vedle kluku, co delali NetBeans a v tymu jsem mel cloveka z JVM; skutecne Scala ma mnohem silnejsi typy, proto jde hodne auto-complete udelat lip, nez u Javy. Java ma typicky problem s type erasure, a Scala ma nektere finty, ktere ji umozni obejit nektere problemy, co z toho plynou. Nekdy je docela prekvapive, jak se v Scala REPL checkne typ, ktery by v Jave nebyl vubec znam, nebo znam az v nakym hodne hlubokym predku. Kdyz delate neco v Sparku, tak ty typy v Scale jsou mnohem mnohem silnejsi, nezli u Javy.
skutecne Scala ma mnohem silnejsi typy
Kdo by to byl rekl, co?
proto jde hodne auto-complete udelat lip, nez u Javy
Jde videt, ze Scalu znas jenom z povidani. Kdybys Scalu opravdu pouzival, vedels bys, ze problemy z toho, ze se odvodi nejaky priserny typ, s kterym si IDE nevi rady, jsou na dennim poradku, ... obzvlast pri debuggovani je to prijemne.
Scala REPL checkne typ, ktery by v Jave nebyl vubec znam, nebo znam az v nakym hodne hlubokym predku.
Zajimalo by me, jakou roli v tom hraje hloubka dedicnosti....
Jinak u C++ by to mohl umět CLion, který má stejně základy jako IDEA. A dost možná to budou umět další IDE. Ono stejně IDE potřebuje znát informace o typech, takže jde jen o to, aby to umělo zobrazit.
U Pythonu je to horší, protože jde o dynamicky typovaný jazyk. Navíc razí filozofii duck typingu, takže nemá ani rozhraní, jako má PHP, což typovému systému taky nepřidá. Sebelepší IDE pak bude leda hádat, což v jednodušších případech pomůže a ve složitějších případech bude beznadějné. Takže tam debugger k tomuto účelu smysl má.
Něco jako v KDevelopu najet na proměnnou myší, to ukáže typ, který se skrývá za auto
, a ten zkopírujete? Neuvěřitelně složité! Jak jen to můžu používat?
Stejně to funguje v IDEA s Lombokem, který přidává typovou inferenci pomocí anotační procesoru do jakékoliv Javy přinejmenším od verze 6.
Já jsem dlouho dělal v Javě, teď dělám nějakou dobu v Typescriptu. Tam je typová inference dost užitečná, ale je to hlavně kvůli odlišnému typovému systému. Ten je sice mocnější než Javový, ale zároveň se v něm dost hůře zjišťuje, co je vlastně špatně. Takže typová inference je nejjednodušší způsob, jak zajistit, že proměnná bude mít správný typ a nemuset zkoumat, jak ho deklarovat, aby byl správně.
v C# var funguje taky a obcas to pouzivam, me je celkem ukradene, co nejaka metoda vraci za typ, staci videt, co za metody a vlastnosti to nabidne za teckou. Zkrati to i zapis, odpadne nutnost mit tolik usingu a pri refaktoru neni potreba pri prejmenovani typu nebo namespacu menit tolik zavislych souboru.
Opruz to je, nicméně rozumné IDE vše toto už řadu let řeší. Jak generování předpisu smyčky (po klávesové zkratce si jen z nabídky vybereš, přes co iterovat), tak vytvoření lokální proměnné z pravé strany (opět jen zvolím název, příp. zadám vlastní, obvykle hned první nabídka IDE bývá ta správná).
Mně nejzajímavější na nové verzi javy přijde uváděný nárůst výkonu.
Jen u C++ je treba si davat pozor na to kde a jak ho pouzit. Nektere konstrukce maji vice moznosti jak vracenou hodnotu interpretovat a ne vzdy je to tak jak byste to zamyslel.
Srovnavat Typescript s JAVA asi neni uplne koser, v typescript neni treba typ znat az do doby, kdy s nim prijemce data pracuje, ne? V JAVA/C++/C#/atp. je to dano v dobe kompilace?
To platí tak pro případ String s = new String() ale když tam bude něco jako NecoInterface n = BlaBlaFactory.buildInstance() tak je inference spíš kontraproduktivní a čitelnost zhoršuje. Jiná věc samozřejmě je, že se blbě čtou věci jako Map<A, <Map<B, C>> ale tady by bylo spíš řešením mít lepší typový systém.
Hlásím, že hlavně zásluhou Lahváče podporu varů už NetBeans mají. Vyzkoušejte https://builds.apache.org/view/Incubator%20Projects/job/incubator-netbeans-release/
PS: Je to jen dev verze, ale to je hlavně tím, že jsme se ještě úplně nesrovnali s Apačím vývojem. Kvalita by měla odpovídat betě.
"Java 9 zaviedla nový Gargage Collector, nazvaný Garbage-First (G1) collector. Aby sa zlepšila latencia pri konkurentnom kóde, bude tento nový GC paralelizovaný". - toto je od zaciatku do konca blbost. To co sa realne bude diat je, ze v pripade ak g1 musi vykonat uplny cyklus pre uvolnenie pamate (comu sa prave g1 snazi vyhnut a nie je to bezna operacia v g1) tak bude tento cyklus bezat paralelne. S konkurentnym kodom toto naozaj nema nic spolocne.
je oracle java a openjdk je ta ista implementacia javy. oracle len prida kopu licencnych ujednani, nejake platene ficury a prebali to.
co sa tyka typovej inferencie lokalnych premennych tak nie som velky fanda. V jave to podla mna nie je treba. tiez napriklad ked budem chcet schovat implementaciu za interface tak sa to neda pouzit lebo inferencia mi urci typ tej implementacie.... napriklad List l=ArrayList()
Doporučuji používat Marlin renderer jak na OpenJDK tak in na Oracle JDK. Od verze 9 je Marlin soucasti OpenJDK i OracleJDK.
https://github.com/bourgesl/marlin-renderer
OpenJDK pouziva Pisces renderer.
Duvodem pro pouziti je, ze defaultni Ductus renderer v Sun/Oracle JDK nebylo mozne uvolnit pod licenci GPL.
Renderujete obrazky paralelne? e.g. na serveru? Pak si zkuste poslat treba 10 pozadavku na renderovani obrazku bez Marlin renderer (Ductus) a pak s Marlin renderer. Defaultni renderer v Sun/Oracle JDK je optimalizovany pro desktop -> jeden obrazek se renderuje rychle, ale ostatni cekaji! Marlin je o trochu pomalejsi (mozna uz ne!), ale kdyz se renderuje vice obrazku paralelne, tak je vyrazne rychlejsi, nez Ductus. Nasi obchodaci si dokonce stezuji, ze az moc :).
No nevim. Napr. stahovani jar/pom souboru pomoci maven z maven.oracle.com (a autententizace pres wagon-http) mi funguje pouze pred Oracle JDK. OpenJdk na RHEL7 porad hlasi invalid password. Drive openjdk napr nepodporovalo webstart. Rozdily tam urcite jsou, je jich malo ale obcas dokazi prekvapit.
Všichni řvou po semantic versioning, ale nikdo ho prakticky nenaplňuje, a když už ho někdo naplňuje, tak se na něj vrhne stádo se slaboduchými argumenty typu "čím víc pruhů, tím víc addidas". To samé stádo pak zpravidla ve svých programech zavádí nekompatibilní změny při přechodu z verze 1.2.8 na verzi 1.2.9, v ideálním případě dohromady s opravou klíčové security chyby.
Neni to zadny jit pro linux. Jit ma java pakticky od jakziva (co ma hotspot) a v sude. Toto je nasazeni gralu. coz je (opravdu experimentalni) JIT naspany v **jave**. Vzheldem k otmu ze 99% vyvojaru jazyka java frci na linuxu, tak byl Graal (stejen jako AOT) nasazen nejdrive na linuxu. tot vse.
Díky xa novinky. Bohužel článek obsahuje nepřesnosti nebo zavádějící formulace:
> JEP 307 – paralelizovaný východzí Garbage Collector
>
> Java 9 zaviedla nový Garbage Collector, nazvaný Garbage-First (G1) collector.
G1 tu už byl mnohem dříve, objevil se v šestce, podporován byl v 7u4: https://en.m.wikipedia.org/wiki/Garbage-first_collector
> JEP 312 – handshakes v lokálnych vláknach
>
> Táto novinka zlepší výkon virtuálnych strojov Javy. Umožní zastavenie jednotlivého vlákna. Doteraz bolo možné ukončiť všetky vlákna naraz alebo neukončiť žiadne.
Z popisu mi to zní jako něco podobného Thread.stop(), ve skutečnosti se to týká safepointů, takže jde o interní záležitost implementace. A jde asi spíš o pozastavení než o úplné zastavení…
> JEP 317 – experimentálny JIT kompiler pre Linux
Z popisu to zní, jako by pro Linux JITka neexistovala. Existuje, jen se experimentálně přidává další varianta, Graal. (To tu už někdo poznamenal.)
IntelliJ IDEA 2017.3 Java 10 podporuje , viz https://blog.jetbrains.com/idea/2017/11/intellij-idea-2017-3-eap-brings-support-for-local-variable-type-inference/