Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailemVyřešeno 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

loading...
Jsou zobrazeny jen nové odpovědi. Zobrazit všechny
Předmět Autor Datum
Pokud vyraz v map vyhazuje checked exception, tak ho zabal do privatni metody a osetri ji v ni. Ose…
MaSo 22.05.2019 13:53
MaSo
Myslíte tohle? public ArrayList<String> getAllFields() { var fields = new ArrayList<>(Arrays.asList…
MichalDM 22.05.2019 20:00
MichalDM
Chceš v prípade exception vrátiť prázdny reťazec? Tak potom áno, ale normálnejšie je zabaliť to do R…
moose 22.05.2019 21:57
moose
Takhle? public ArrayList<String> getAllFields() { var fields = Arrays.stream(getClass().getDeclared…
MichalDM 23.05.2019 00:03
MichalDM
public List<String> getAllFields() { return Arrays.stream(getClass().getDeclaredFields()) .filter(fi…
moose 23.05.2019 00:07
moose
Aha. To mě mohlo napadnout. Proč vytvářet dvě proměnné, když to můžeš hned vrátit. Ano, static tam…
MichalDM 23.05.2019 11:42
MichalDM
To nie že NetBeans nepočíta s null, ale ten program nepočíta s null. Ak "field.get(this)" vráti null…
moose 23.05.2019 17:29
moose
Aha. public ArrayList<String> getAllFields() { return Arrays.stream(getClass().getDeclaredFields())… poslední
MichalDM 26.05.2019 00:08
MichalDM

Myslíte tohle?

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 -> mapping(f)).forEach(f -> list.add(f));
    return list;
}

private String mapping(Field field) {
    try {
          return field.get(this).toString();
    } catch (IllegalAccessException | IllegalArgumentException e) {
          return "";
    }
}

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?

public ArrayList<String> getAllFields() {
    var fields = Arrays.stream(getClass().getDeclaredFields());
    var list = fields
              .filter(f -> !Modifier.isStatic(f.getModifiers()))
              .map(this::getFieldValue)
              .collect(Collectors.toCollection(ArrayList::new));
    return list;
}
    
private String getFieldValue(Field field) {
    try {
        return field.get(this).toString();
    } catch (IllegalArgumentException | IllegalAccessException e) {
        return "";
    }
}

Nepochopil jsem to s tou RuntimeException respektive jak to napsat?

Díky

public List<String> getAllFields() {
    return Arrays.stream(getClass().getDeclaredFields())
              .filter(field -> !Modifier.isStatic(field.getModifiers()))
              .map(this::getFieldValue)
              .collect(Collectors.toList());
}
    
private String getFieldValue(final Field field) {
    try {
        return Objects.toString(field.get(this));
    } catch (final IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

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.

.filter(field -> !field.equals("null"))

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 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.

public ArrayList<String> getAllFields() {
               return Arrays.stream(getClass().getDeclaredFields())
                           .filter(field -> !Modifier.isStatic(field.getModifiers()))
                           .map(this::getFieldValue)
                           .filter(Objects::nonNull)
                           .map(Objects::toString)
                           .collect(Collectors.toCollection(ArrayList::new));
}

private Object getFieldValue(final Field field) {
      try {
            return field.get(this);
      } catch (final IllegalAccessException e) {
            throw new RuntimeException(e);
      }
}

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

Zpět do poradny Odpovědět na původní otázku Nahoru

loading...