lunedì 15 luglio 2019

Java Reflection e CSV

Cosa sono le reflection? Sono sostanzialmente un modo per ottenere tutti i nomi dei metodi, ed eventualmente invocarli, senza sapere quali siano questi metodi. Tutti sanno da codice invocare il metodo "getPippo()" di una certa classe scrivendolo da codice, ed e' quello si fa praticamente di normale. Pero' ci sono delle situazioni per le quali non sai il nome dei metodi da invocare (oppure non li vuoi sapere perche' sono centinaia e devi invocarli tutti).

Java ci mette a disposizione uno strumento potentissimo (se usato bene): le reflections. E' possibile da un oggetto Classe ottenere i nomi dei suoi metodi (come oggetto Method). Tramite questi nomi, e' possibile invocarli per esempio, ma e' necessaria un'istanza di quella classe (ovviamente). E' anche possibile passare argomenti.

Il caso che tratteremo qui e' la generazione di un csv. Il caso che mi ha spinto a creare una classe generica e non una ad hoc e' che la tabella (e quindi l'entity) della quale devo generare il csv ha una pletora di campi (2 o 300 tipo) per cui la decisione era:

  • spendo 2h a fare un csv con possibilita' altissima di sbagliare (visto l'elevato nr. di campi)
  • spendo 10min a farlo con una reflection con la possibilita' poi di usarlo per tutto?

La domanda e' semiretorica anche perche' se non avessi scelto la seconda opzione non sarei qui a scrivere questo articolo. Per arrivarci pero' devo fare un po' di premesse:

La prima e' un metodo "generico" che formatta un oggetto usando istanceof (e restituisce una stringa) controllando pure se e' null o not. Se l'oggetto che mi passo in input e' null restituisco stringa vuota (questo perche' in un csv devi comunque mettere un ; se vuoi un campo vuoto).



Poi ci serve (o almeno a ME!!!) un metodo per convertire il camel case in stringa "normale" - questo perche' uso i nomi dei metodi come nomi di colonne (non vorrai mica fare un csv senza intestazione). Ma questo si fa facile - StringUtils di apache ci viene incontro con splitByCharacterTypeCamelCase il cui risultato passeremo al metodo join sempre di StringUtils (non metto screen abbiate pazienza).

Fatto questo io mi sono fatto un metodo di utilita' che data una classe restituisce tutti i suoi getters (escluso getClass che nel csv non serve)

Poi ci serve un metodo di utilita' che restituisca una stringa csv di una certa istanza (generica). Ho usato i generics perche' voglio potergli passare di tutto. Come si puo' notare per invocare un metodo tramite reflection si invoca il metodo invoke passandogli un'istanza che e' quella sulla quale il metodo verra' eseguito il risultato e' un object. Mi passo anche il separator (perche' voglio essere flessibile).

Come si puo' vedere viene invocato il metodo "formattatore" generico presentato all'inizio, questo evita che nella stringa ci sia il toString degli oggetti (che non e' molto human readable). Inoltre, sempre questo metodo, evita che ci siano dei campi sfalsati nel csv (dovuto alla presenza di campi null).

Vi risparmio la parte di scrittura su file perche' non interessante ai fini di questo articolo.

Alla prossima!

Nessun commento:

Posta un commento

I messaggi non appaiono subito ma a seguito dell'approvazione di un moderatore. Siete pregati di seguire le seguenti regole