Funkcje naobiektach a nie tylko streamów

1

Pracując w Kotlinie bardzo spodobało mi się to że nie tylko dla streamów ale też na obiektach można wywołać funkcje (let itp).
Pytanie czy można coś takiego zrobić w Javie?
Póki co jedyne na co wpadłem to żeby opakować obiekt w Optionala i wówczas wywoływać funkcje, np.:

    @GetMapping("/some")
    public ResponseEntity<SomethingDto> getSome() {
        return Optional.ofNullable(some.getSomething())
                .map(some::doSomething)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());

Niby spoko ale jakieś to takie dziwne żeby wszystko w Optionala ładować. Jakieś lepsze pomysły?

@Edit dla @jarekr000000
Mając jakiś strumień w Javie, mogę go sobie przetwarzać w taki sposób:

Stream<Customer> customers = customerList.stream()
       .map(...)
       .filter(...)
       .map(...)
        .....

Jest to bardzo wygodne i przejrzyste - wiadomo.
Jeśli spojrzymy na Kotlina to można robić coś podobnego ale nie mając kolekcji, np.:

    call.receive<CustomerCmd>()
        .let { customers.save(it) }
        .let { resolve(it) }
        .run { call.respond(statusCode, body) }

I teraz chciałbym uzyskać coś podobnego w Javie i się zastanawiam czy to w ogóle możliwe. Mając Optionala mam już jakiś tego zalążek ale zastanawiam się czy można to zrobić lepiej.

0

Ciekawe pytanie, ale z tego co się orientuję bez jakiejś szalonej magii się nie obejdzie.
Kotlin ma wiele ulepszeń jeśli chodzi o domknięcia i wydaje mi się, że na tym bazują scope functions.
Pozostaje ewentualnie method reference

2
eL napisał(a):
    @GetMapping("/some")
    public ResponseEntity<SomethingDto> getSome() {
        return Optional.ofNullable(some.getSomething())
                .map(some::doSomething)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());

Ja trochę nie rozumiem problemu. Ten przykład ma sens jeśli getSomething zwraca faktycznie null. W przeciwnym wypadku, to można użyć przecież po prostu metod i lokalnych zmiennych. Z var to już w ogóle nie trzeba pisać jakichś epopei.

    call.receive<CustomerCmd>()
        .let { customers.save(it) }
        .let { resolve(it) }
        .run { call.respond(statusCode, body) }

Natomiast ten przykład to moim zdaniem nadużywanie języka. Jest to używanie let i run (w ogóle czemu akurat run? czy ten drugi call należy do wyniku resolve(it)?) dla samego ich używania, a nie dlatego, że coś wnoszą. Równie dobrze (i moim zdaniem czytelniej) można zapisać to w ten sposób.

val customerCmd = call.receive<CustomerCmd>()
resolve(customers.save(customerCmd))
call.respond(statusCode, body)

Owszem, w Javie nie da się np. sprawdzać null za pomocą let albo używać let w niektórych kłopotliwych miejscach, kiedy mamy lekko niedorobione API. Albo korzystać z apply do inicjalizacji obiektów, ale mam wrażenie, że te przykłady starają się niepotrzebnie korzystać z lukru.

1

Wiem o co Ci chodzi. java.util.Function i identity będzie Twoim przyjacielem. Wynalazłeś point free w wersji na javę/ kotlin.

Edit (mam chwikę więc przykład):

 Function
                .<CustomerCmd>identity()
                .andThen(customers::save)
                .andThen(this::resolve)
                .andThen(... )
                .apply(call.receive());

Jak widać jest tylko zmiana na kolejności początkowego elementu - składamy funkcję i na koniec podajemy od czego zacząć.

0
jarekr000000 napisał(a):

Wiem o co Ci chodzi. java.util.Function i identity będzie Twoim przyjacielem. Wynalazłeś point free w wersji na javę/ kotlin.

Edit (mam chwikę więc przykład):

 Function
                .<CustomerCmd>identity()
                .andThen(customers::save)
                .andThen(this::resolve)
                .andThen(... )
                .apply(call.receive());

Jak widać jest tylko zmiana na kolejności początkowego elementu - składamy funkcję i na koniec podajemy od czego zacząć.

@jarekr000000 W sumie to nie do końca to chciałem uzyskać (ten przykład z Kotlina to tylko był po to żeby pokazać że chcę przetwarzać obiekt w taki funkcyjny sposób) natomiast Twoja wypowiedź natchnęła mnie i skorzystałem z Function z Vavr:

  @GetMapping("/something")
    ResponseEntity<SomeDto> getSomething() {
        return Function0.of(some::getSomething)
                .andThen(converter::convert)
                .andThen(ResponseEntity::ok)
                .get();
    }

i to już mnie satysfakcjonuje chociaż to get na końcu jest takie średnio bym powiedział... No ale lepiej lepiej także jeszcze coś poszukam i pójdę w tę stronę :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1