Nullable Reference Types – tip de dată referință care poate fi null (în anumite situații este acceptabil). În Java nu știu să existe așa ceva, cu toate că a apărut Helpfull NullPointerExceptions care oferă un mesaj mai clar asupra câmpului sau metodei care întoarce null
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "String.toLowerCase()" because the return value of
"com.baeldung.java14.npe.HelpfulNullPointerException$PersonalDetails.getEmailAddress()" is null
at com.baeldung.java14.npe.HelpfulNullPointerException.main(HelpfulNullPointerException.java:10)
Null-Conditional Operator(?. ) – prezent de vreo 5 ani și recent adăugat în JavaScript. Java a rămas în urmă, deși au fost discuții. Alături l-aș include și pe null coalescing operator (??)
Interpolare de string-uri – adio `String.format()` 🙂 Cel puțin pentru o treabă simplă. Totuși, Java 13 a introdus Text Blocks și în sfârșit se poate scrie un query SQL sau json fară a concatena sau a escapa 😁
Aș fi vrut sa menționez și var, dar intre timp au apărut 7 versiuni de Java în 3 ani și ceva. În acești ani, limbajul a evoluat și a mai redus din verbozitate (nu există cred acest cuvânt în limba română).
Acum, ce-i mai sus este ce mi-ar fi mie mai util in munca mea de zi cu zi. Pentru unii, Kotlin este raspunsul la cerințele de mai sus, dar nu (cred că) toată lumea vrea să schimbe limbajul pentru 3, 4 lucruri care cresc productivitatea. 🙂
Ar mai fi câtevaproiecte de interes, dar momentan sunt în lucru.
În concluzie, de data aceasta, am apăsat butonul de Publish
Am observat anumite formate de dată și timp care nu pot fi citite corecte cu api-ul JAXB din Java. Mai bine spus, nu pot fi citite, adică acel câmp mapat în clasa din aplicație este null.
Un exemplu de format de dată și timp cu timezone ar fi acesta 2020-10-13T12:25:00+0200.
In aplicație, campul aferent timestamp-ului are ca tip de data XMLGregorianCalendar, o clasă destul de veche folosită pentru a reprezenta timestamp-uri. Clasă veche și un format de timp mai deosebit din punct de vedere al timezone-ului nu fac casă bună și când am pornit testul pentru a vedea dacă se poate citi acel fișier, a ieșit o foarte frumoasă exceție NullPointerException.
Soluția cea mai la îndemană a fost să înlocuiesc XMLGregorianCalendar cu String și astfel citirea se face corect, eventualele erori, cum ar fi lipsa timestamp-ului sau invaliditatea lui, rămânând de tratat la un nivel mai sus, al aplicației.
O altă soluție ar mai fi screrea unui XmlAdapter custom care poate fi folosit pentru a citi corect acel câmp de dată din fișier.
Un exemplu, în cazul problemei de față ar fi acesta:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Deși aveam în plan să scriu despre ele, dar având în vedere că au trecut vreo 4 luni și ceva decând au apărut și nu am ce să aduc nou, m-am gândit să vad cum se descurcă cu una din cele mai comune lucruri într-o aplicație, deserializarea unui răspuns json.
Ca API vom folosi unul dummy care va returna o lista de ToDo-uri cu true sau false, în funcție dacă au fost efectuate sau nu și le vom agrega după acestă condiție.
Primul lucru este să creăm un record care să aibă câmpurile json-ului din răspuns.
Deci record-ul va avea un userId și un id de tip int, precum un câmp title de tip string si un completed boolean.
record Todo(
@JsonProperty("userId")
int userId,
@JsonProperty("id")
int id,
@JsonProperty("title")
String title,
@JsonProperty("completed")
boolean completed) {}
Întrucât folosim Jackson pentru deserializare și record-urile fiind o funcționalitate în preview, va fi nevoie să specificam că acele câmpuri sunt chiar cele din json. Fără ele, Jackosn va arunca o exceptie:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException
Cu record-ul creat după strucutura json-ului, este nevoie să facem call-ul la api, folosind clinetul http.
String DATA_URL = "https://jsonplaceholder.typicode.com/todos";
var httpClient = HttpClient.newHttpClient();
var httpRequest = HttpRequest.newBuilder()
.uri(URI.create(DATA_URL))
.build();
var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
var body = httpResponse.body();
Codul este simplu, nu are nimic special. Întrucât este un raspuns de tip text, sunt interesat să îl primesc așa cum este și îl pun într-o variabilă
Urmează etapa de deserializare și agregare
var mapper = new ObjectMapper();
Todo[] todos = mapper.readValue(body, Todo[].class); //because in json representation, there is an array of todos
//System.out.println(todos[1].title());
var doneTodos = Arrays.stream(todos)
.filter(Todo::completed)
.count();
System.out.println("Completed: " + doneTodos);
System.out.println("Not completed: " + (todos.length - doneTodos));
Avem un array de todo-uri, lucru pe care trebuie sa il specificam metodei readValue(), împreună cu ceea ce vrem să deserializăm, în cazul nostru, variabila body, mai precis, conținutul ei.
Urmează o simplă filtrare dupa todo-urile completed. Aici se vede unul din avantajele de a folosi record-uri. Avem acces la propietați, exect ca la clase cu getteri și setteri, dar în cazul record-urilor sunt generate de compilator. Plus altele.
Marele final reprezinta afișarea la consolă 🙂
Ca pe final de articol, adaugarea lor în Java este binevenită și va reduce mult zgomotul de cod. Codul complet:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
De vreo 2 săptămâni, meșteresc la o aplicație de vizualizare a evoluției cazurilor de COVID19 în lume și în țară. Față de altele, este mai simplistă, dar pentru mine este un exercițiu de a prezenta datele într-o manieră ușor de înțeles de către toată lumea și de a experimenta diferite concepte de programare.
Stack-ul folosit cuprinde Java, Spring Boot și o bază de date MySQL, pe backend, iar pe frontend Bootstrap pentru layout, DataTables pentru tabelele din aplicație și HighCharts pentru chart-ul cu evoluția globală a cazurilor. Datele pentru chart sunt stocate în baza de date.
Sursele de date folosite sunt:
* CSV-ul cu situația la nivel global. Mai multe informații precum și alte csv-uri cu diferite agregări de date aici
Pentru satisticile cu evoluția în țară, am folosit api-ul pus la dispoziție de comuitatea geo-spațial.org
Aplicația este open source și codul poate fi văzut aici, iar acesta este live aici.
Sintaxa este asemănatoare, dacă nu identică cu ce este in Kotlin și Python. Încă nu există conceptul de string interpolation ca în C# sau Javascript, darrrrr este binevenit și acest lucru. Văd utilitatea acestei construcții în query-urile sql complexe. Dacă mai intră în joc și parametrii, se poate folosi String#format
var query = String.format("""
select * from table where name = %s
""", name);
var query1 = """
select * from table where name = %s"""
.formatted(Main.class.getName());//deprecated method
System.out.println(query);
System.out.println(query1);
Rezultatul este următorul:
select * from table where name = cosmin
select * from table where name = com.company.Main
Mai multe lucruri despre aceast feature si o analiză mai detaliată, in acest link
Să zicem că avem următoarea secvență de cod care folosește cuvântul cheie var, introdus în Java 10 și proiectul are suport Maven.
Dar surpriză !
Programul nu compilează, iar in consolă apare urmatoarea eroare:
Error:java: error: release version 5 not supported
După o căutare simplă, aflu că Maven setează Java la versiunea 5, pentru a păstra compatibilitatea.
Soluția simplă și cea mai la îndemână, a fost să modific in fișierul compiler.xml din directorul .idea(se vede in IDE), <module name=”untitled” target=”1.5″/> în <module name=”untitled” target=”10″ />, pe scurt o versiune mai nouă a limbajului care suportă feature-ul respectiv.