

Java - ArrayList - Stream - Jak vložit kód do try-catch
Dobrý den,
Když použiji tento kód
public ArrayList<String> getAllFields() {
var fields = new ArrayList<>(Arrays.asList(getClass().getDeclaredFields()));
var list = new ArrayList<String>(fields.size());
fields.stream().filter(f -> !Modifier.isStatic(f.getModifiers()))
.map(f -> f.get(this).toString()).forEach(f -> list.add(f));
return list;
}
tak to řve, že v map může dojít k chybě.
Jak to ale ošetřím? Když použiji přímo NetBeans (Surround Statement with try-catch), tak mi vygeneruje
public ArrayList<String> getAllFields() {
var fields = new ArrayList<>(Arrays.asList(getClass().getDeclaredFields()));
var list = new ArrayList<String>(fields.size());
fields.stream().filter(f -> !Modifier.isStatic(f.getModifiers()))
.map(f -> {
try {
f.get(this).toString();
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
}
}).forEach(f -> list.add(f));
return list;
}
což je taky špatně viz příloha. (Při spuštění to zhavaruje s chybou Uncompilable source code - Erroneous sym type: java.util.stream.Stream.map.forEach)
Co, prosím Vás, dělám špatně?
Děkuji
Pokud vyraz v map vyhazuje checked exception, tak ho zabal do privatni metody a osetri ji v ni.
Osetrit v tomhle pripade znamena zabalit do RuntimeException...
Myslíte tohle?
Chceš v prípade exception vrátiť prázdny reťazec? Tak potom áno, ale normálnejšie je zabaliť to do RuntimeException, t.j. v catch bude throw new RuntimeException(e). V takom prípade ti stačí chytať IllegalAccessException, keďže IllegalArgumentException už je zdedená z RuntimeException.
Mimochodom, ak lambda funkcia iba volá nejakú metódu s pôvodným parametrom, tak môžeš použiť rovno referenciu na metódu. V tomto prípade "f -> list.add(f)" môžeš zapísať ako "list::add" a rovnako "f -> mapping(f)" môžeš zapísať ako "this::mapping". Tá metóda mapping by mala byť statická, takže potom by to mohlo vyzerať ako "MojaTrieda::mapping". Inak, to čo je za názov metódy, že "mapping"? Tam by sa skôr hodilo niečo ako "getFieldValue" alebo niečo podobné.
Inak namiesto ".forEach(f -> list.add(f))" môžeš rovno zozbierať namapované hodnoty do listu, t.j. niečo typu list = fields.stream().filter(...).map(...).collect(Collectors.toList()). (+ definovať niekde typ)
Ďalej namiesto robenia listu z poľa a následne arraylistu z listu, z ktorého aj tak nakoniec spravíš stream môžeš rovno spraviť z poľa stream pomocou metódy "Arrays.stream".
Keď každé zreťazené volanie dáš na nový riadok, tak to bude aj čitateľné.
Takhle?
Nepochopil jsem to s tou RuntimeException respektive jak to napsat?
Díky
Edit: Som si nevšimol, že mapping používalo this, takže to s tou statickou metódou beriem späť, ale to si si asi už všimol.
Aha. To mě mohlo napadnout. Proč vytvářet dvě proměnné, když to můžeš hned vrátit.
Ano, static tam nešlo napsat. Ale, NetBeans jaksi nepočítá, že field může být null a zhavaruje to. Tvoje verze metody getFieldValue sice nezhavaruje, ale vrátí to null.
Pokud to budu chtít odstranit, musím za map přidat filtr.
Ale nechápu tvojí úpravu druhé metody. Proč jsi použil třídu Objects a final?
Díky
UPDATE: Ještě jedna věc. Bylo by možné upravit metodu tak, aby vrátila pouze celá čísla a řetězce?
U čísel by to bylo nejjednodušší pomocí NumberFormatException, které by se dalo do druhé metody?
To bych raději do samostatné otázky.
Odkaz
To nie že NetBeans nepočíta s null, ale ten program nepočíta s null. Ak "field.get(this)" vráti null, tak sa potom volá "null.toString()", ktoré spadne. "Objects.toString" počíta aj s prípadom, že hodnota je null, pričom ako druhý parameter môžeš nastaviť predvolenú hodnotu, ktorá sa použije v prípade null.
Áno, pre odstránenie null hodnôt je vhodné použiť filter. Ale keďže nechceš odfiltrovať reťazce, ktoré obsahujú text "null", tak je lepšie to spraviť predtým, než z hodnoty toho poľa spravíš reťazec. Niečo typu: "fields.map(this::getFieldValue).filter(Objects::nonNull).map(Objects::toString)...", pričom "getFieldValue" v tomto prípade vráti priamo objekt z "field.get(this)" bez konverzie do reťazca.
Druhá možnosť by bola použiť "Objects.toString(field.get(this), null)" a následne filtrovať podľa hodnoty null. Ale tá prvá možnosť je vhodnejšia.
Pre otestovanie typu hodnoty môžeš použiť "instanceof" alebo metódu "isAssignableFrom". Pre tento prípad bude tiež lepšie, ak "getFieldValue" vráti priamo objekt bez konverzie do reťazca. Potom je už len na tebe, či budeš testovať na Integer, Long, Byte a pod.
Final som použil preto, pretože je dobrou praktikou nemeniť hodnoty premenných a final zabezpečí, že to tak naozaj aj bude. Keď vidím final, tak nemusím čítať celý kód medzi priradením hodnoty do takej premennej a riadkom, kde sa tá premenná používa, a môžem si byť istý, že tam je hodnota priradená pôvodne.
NumberFormatException by si mohol použiť, ak by si chcel overiť, že textová hodnota reprezentuje číslo. Ak máš v poli ale priamo číslo, tak to moc nepomôže.
Aha.
Tímhle kódem bych to uzavřel. Pokud nemáte návrhy na zlepšení.
Přidání filtru na určité typy bych dal do samostatné otázky. viz odkaz výše.
Díky