2016-12-28 42 views
7

Załóżmy, że mam metodę biblioteki tak (bardzo skrótowo):Co powrócić, gdy metody generyczne zwracają "nic", ale wartość null nie może zostać zwrócona?

public static <V> Optional<V> doSomethingWith(Callable<V> callable) { 
    try { 
     return Optional.of(callable.call()); 
    } catch (Exception ex) { 
     // Do something with ex 
     return Optional.empty(); 
    } 
} 

I chcę coś, co nie zwraca wartości, jak:

Library.</*What1*/>doSomethingWith(() -> { 
     foo(); 
     return /*what2*/; 
    }); 

Mój pierwszy instynkt rodzajowy metoda, która nie zwraca wartości, tworzy typ Void i zwraca null, jednak ponieważ wynik zostanie zawinięty w Optional, to spowodowałoby to wyjątek.

Jakie są uzasadnione elementy zastępcze dla /*What1*/ i /*what2*/, które nie wyglądają zupełnie przypadkowo, jak Integer i 0?

[edytuj] Próbuję uniknąć Optional.ofNullable, ponieważ puste jest używane tutaj, aby wskazać, że callable.call() nie zakończyło się normalnie.

+3

Nie można zmienić metody biblioteki, aby użyć opcji "Opcjonalny.Niewylny"? Jaki jest sens, aby zwrócić opcjonalne, jeśli jest zawsze obecny? –

+0

Metoda biblioteczna jest bardzo skrócona. W rzeczywistości może zwrócić 'Opcjonalny.empty()', gdy wystąpi warunek. –

+0

Można po prostu dodać lokalną klasę o rozsądnie określonej nazwie i zwrócić instancję tej klasy. – glee8e

Odpowiedz

4

Zazwyczaj używam Boolean.TRUE, aby oznaczyć sukces, ale możesz również zwrócić Void.class. Obie są tanie w tym sensie, że nie każdy powrót tworzy nowy obiekt do odrzucenia. Chociaż Class<Void> to nie tylko Void, może również służyć do oznaczania czegoś jako void.

Jak już wspomniano, można również utworzyć własną klasę wyników/-enum.

Możesz też oczywiście zwrócić Optional.<Void>nothing(). Spowoduje to, że niektóre Optional<Optional<Void>>, ale także zrób.

Jeśli uważasz, że wszystkie powyższe elementy są brzydkie, obawiam się, że API prawdopodobnie nie jest dobrze dopasowane do Twoich potrzeb. Podnieś prośbę o wydanie/wycofanie lub poszukaj czegoś innego.

+0

Najbardziej lubię opcję wyliczania. Szczególnie dlatego, że wczoraj zastanawiałem się tylko, dlaczego nie ma wbudowanego (nie-prymitywnego) typu, który ma dokładnie 2 możliwości. Aby porównać: 'Void' ma jedną możliwość:' null', a 'Boolean' ma trzy:' null', 'FALSE',' TRUE'. ("Obiekt" ma oczywiście nieskończone możliwości). –

6

Jeśli potrzebujesz podpowiedzi dotyczącej ogólnego parametru, który nigdy nie zostanie użyty, możesz użyć Void, JDK robi to również w niektórych przypadkach, np. przy konwersji Runnable do CompletableFuture<T> używa Odpada dla T.

Jeśli używasz Optional.ofNullable następnie można po prostu wrócić null dla what2, który jest ważny tylko wartość dla Void.

[edytuj] Próbuję uniknąć Opcjonalne.Niewakład, ponieważ puste jest używane tutaj, aby wskazać, że callable.call() nie ukończył normalnie.

Następnie używasz niewłaściwego narzędzia do pracy. CompletionStage lub CompletableFuture ma odpowiednią semantykę.

0

Użyj typu Void dla typu zwracanego, który jest logicznym wyborem dla "nic", ale w rzeczywistości zwraca instancję z Void.

Chociaż javadoc for Void mówi, że:

... An uninstantiable klasa zastępczy ...

Można mimo instancję:

try { 
    Constructor<Void> c = Void.class.getDeclaredConstructor(); 
    c.setAccessible(true); 
    return c.newInstance(); 
} catch (Exception perfunctory) { 
    return null; // won't happen 
} 
+6

Zła rada. Przerywasz kilka kontraktów, kiedy włamujesz się do instancji 'Void' do istnienia, nie wspominając o możliwych zmianach implementacji w przyszłych/alternatywnych pakietach JDK, które powodują, że' nie wydarzy się tak czy inaczej '. –

+0

@mark które umowy, ** w szczególności **, są zepsute? Proszę podać linki itp. Uważam, że nie ma takich umów i że jeśli przyszłe wersje zmienią konstruktora tak, że ten kod nie zadziała (bardzo mało prawdopodobne), martw się o to. – Bohemian

1

Można również tworzyć własny typ podobny do Void

public class Result { 
    public static final Result OK = new Result(); 

    private Result(){} 
} 

a następnie zwróć Result.OK.

Możesz również ulepszyć ten typ, aby również reprezentować błędy, jeśli potrzebujesz.

Ale może przy użyciu Java Void jest lepiej, jeśli nie potrzebujesz niczego specjalnego.

+0

Używałbym do tego wyliczenia. – TheConstructor

+0

To zależy od tego, co musisz zrobić, z singleton nie możesz zarządzać BŁĘDU, ponieważ z nim będziesz potrzebował innej instancji za każdym razem z inną wiadomością. – rascio

+0

Enums dziedziczy kilka innych metod (np. 'ToString'), dlaczego zwykle wolę je dla instancji sygnalizujących, takich jak proponowane' Result.OK'. Problem OP nie istniałby, gdyby niektóre klasy "ResultOrError" zostały użyte w API zamiast "Opcjonalnie". – TheConstructor