Ekkora regényre nem számítottam, persze hogy csak koncepció szintjén írtam le a gondolataimat, minden egyes témáról sokat lehetne írogatni, gugli segít a bővebben kifejtett válaszok megtalálásában :).
A property hasznos dolog, egyrészt reflection-nél is jól tud jönni hogy egyben van a get/set, másrészt szerintem sokkal olvashatóbb egy értékadás az erre szolgáló operátorral (=), mint set/get függvényhívásokkal. Ez egy plusz absztrakció, pl. egy értékadásnál bármikor kitalálhatod, hogy mégiscsak kell valami ellenőrzést végezni, és ilyenkor az objektum interfésze nem változik, nem kell refactorálni mindent =-ről set()-re. Arról nem is beszélve, hogy külső libekkel sokszor beanekkel tudsz nomrálisan interaktálni, field-ekkel nem annyira (főleg ha van SecutityManager). A láthatóság szintén nem mindegy, a globálisan írható mezők kissé veszélyesek tudnak lenni, még saját kódbázison belül is. Ha valamit lehet írni kívülről, akkor előbb-utóbb az egyik fejlesztő írni is fogja, aztán majd lehet hibát nyomozni.
A checked exception kapcsán pedig egyetértek veled teljesen, csakhogy az évek alatt sokszor tapasztaltam azt, hogy a kikényszerített exception kezelés eredménye nem a kezelés lesz, hanem az exception lenyelése. A legrosszabb dolog, amit egy exception-el tehetsz az, hogy elkapod de nem kezeled le. Ha a drága egységsugarú fejlesztőnek implementálnia kell egy metódust, aminek a során ő találkozik egy checked exception-el, de az interfész, amit implementálnia kell nem definiálja, akkor három eset áll fenn: vagy szól a tervezőnek, hogy ide kellene még egy exception, vagy csomagol, RuntimeException-be vagy valami másba. A harmadik eset az, hogy elkapja, majd lehetőleg ki sem loggozva visszaad valami hülyeséget. Na ilyen kódban próbáld megtalálni a hibát, sok sikert. Ennél jobb koncepció az, hogy a legkülső ponton (pl. uncaughtexceptionhandler, threadpool thread-jei, stb) mindenképpen naplózzuk az exception-öket, és megpróbálunk valami helyreállítást csinálni. Ezt a lépést mellesleg mindenképpen meg kell csinálni, és így legalább a kevesebb tapasztalattal rendelkező fejlesztő sem fogja megszívatni az egész csapatot.
Sokat dolgoztam C#-ban, és ott nincsenek checked exception-ök. Eleinte hiányoztak nekem is, de aztán rájöttem, hogy nem nyersz vele annyit, mint amennyit bukni tudsz. Persze a .NET-ben a dokumentációban részletesen és konzekvensen le vannak írva a dobható exception-ök, ez sokat segít. A RuntimeExcepotion-be csomagolt alkalmazás hibák viszont nem segítenek.
Egyszerű típusok a teljesítmény és a natív libek interakciója miatt léteznek. Mivel nem voltam ott a nyelv tervezésekor, és nem olvastam utána, ezért nem tudom, mi vezetett ahhoz a döntéshez, hogy ezek kezelése a Java-ban teljesen más, mint az objektumoké. Pl. a C#-ban ez meg van oldva, azaz egységes az API és mégis van jó teljesítmény. Az autoboxing (java 5-től van) az inkább csak a probléma elfedése, gyakorlatilag az egyszerű típusok által nyújtott teljesítményt bukjuk el vele, hogy ne fájjon collection-ökbe pakolni őket. A List<Integer> és az int[] között jelentős a különbség, sajnos API szempontjából is. Tiszta szerencse, hogy legalább a foreach működik tömbökkel.
Az operator overloading nagyon veszélyes is tud lenni (lásd Scala), és tényleg nem egy akkora durranás, viszont milyen jól jönne pl. a BigDecimal használatánál ugyebár. Vagy a Matrix-nál. Ahol sok az aritmetika, na azt kimondottan fáj Java-ban csinálni az operátorok hiánya, a property-k hiánya és az automatikus típuskonverzió miatt. A string + az nem build optimization (oké, annyira azért mégis, hogy StringBuilder lesz belőle), hanem a + operátor felül van bírálva string-ekre. Ha már nincs overload, akkor miért van mégis overload-olva a + ? Jó, én örülök neki, hogy van ilyen, de még jobban örülnék, ha BigDecimal-ra is lenne. Ami viszont nagyon hiányzik, az asszociatív tömb-szerű indexelés. Nagyon jó lenne, ha lenne, de hát nincs overload, csak a String-re (kivéve a gyevi bíró, ja)
A var-os példádban nem a var kulcsszóval van baj, hanem a type inference fos, amit speciel említettem is korábban, hogy nem elég jó :) Az első példádnak ott fordítási hibát kellene dobnia (nem tudom, nem próbáltam a 10-es Java-t). Kotlinban, C#-ban pl. az ilyen kód nem fordul. A számokkal pedig szintén nem lenne gond, ha a java nem konvertálna automatikusan, number literalok pedig igenis vannak a nyelvben (pl. 0.1d = double és nem float). A dadogós "EzEgyNagyonHosszúClassNév változó = new EzEgyNagyonHosszúClassNév(dsjhdks)" viszont kimondottan gáz, na erre való a var kulcsszó.
A nyelvvel van bajom elsősorban, a runtime többnyire rendben van, legalábbis nem rosszabb a többieknél. Persze pl. a datetime api-nál miért kellett várni a 8-as verzióig, és ha már a jodatime-ot vették át, akkor miért maradt ki egy-két metódus, illetve ennyi erővel maradhatott volna a package, az már jó kérdés.
Az ökoszisztéma miatt pedig kifejezettem kedvelem a JVM világot, csak minél többet használok jobban kézreálló nyelveket, annál jobban fájnak a Java hibái. Ezért örülök különösen a Kotlin-nak, mert habár itt is vannak hülyeségek (pl. Unit visszatérési érték, stb), de olvashatóbb a kód és hamarabb meg tudom benne írni, amit kell. És persze 100% kompatibilis a Java libekkel/classokkal, enélkül nem lenne jó :)
Ja, az API bugok: na erre még nem találtam magyarázatot, hogy a Map<K, V> esetén a put(Object, V) kijavítása put(K, V)-re mégis mily módon okozhatna visszamenőleges kompatibilitási problémákat? Ha emiatt nem fordulna a kód, ott az eleve hibás volt! (valójában de, tudok ilyen hibás kódot írni, ami mégsem fog exception-t dobi, de bakker, ez a type erasure miatt csakis compile-time érdekes, runtime nem. áááá)