2011-12-16 8 views
13

Piszę konkretne wyjątkowe miotacze w przypadku braku Opcji.Skrót do Guava Opcjonalne użycie z wyjątkami?

Na przykład:

Optional<?> optional = ...; 
if (!optional.isPresent()) { 
    throw new MyException(); 
} 
Object result = optional.get(); 

znajdę ten kod nie bardzo płynnie, zwłaszcza korzystanie z hukiem (!). Chciałbym napisać coś w stylu:

Optional<?> optional = ...; 
Object result = optional.orThrow(MyException.class); 

Czy istnieje taki skrót w Guava, że ​​jeszcze nie znalazłem?

+6

Pytanie ogólne. Jeśli sprawdzana jest opcja Opcjonalna, czy nie jest to sprzeczne z koncepcją "opcjonalnie", aby mieć metodę taką jak "Opcjonalny.orowy"? –

+4

Całkowicie się zgadzam. Jeśli "isAbsent" (dodany w wersji 11) jest poprawnym warunkiem, nie powinno powodować wyjątku. Punkt "Opcjonalny" oznacza, że ​​nieobecność jest ważnym, nie wyjątkowym przypadkiem. Jeśli nieobecność jest wyjątkowa, twoja metoda powinna po prostu zwrócić przedmiot i rzut i wyjątek w nieobecnym przypadku. –

+2

@ black panda: dlaczego mogę podać wartość domyślną za pomocą metody 'or()'? Jest to rodzaj pozwalający na zachowanie domyślne, dlaczego nie inne? @ John B: Jednym z przypadków użycia jest dość ogólny ekstraktor atrybutów XML: ten atrybut może być obecny lub nie, więc metoda zwraca wartość "Opcjonalnie". Następnie wywołujący może wymusić obecność wartości (rzucając wyjątek), lub uzyskać wartość, jeśli istnieje, lub nawet pobrać wartość domyślną. Metoda ekstraktora po prostu nie wie, co osoba wywołująca chce zrobić z wartością, więc musi zwrócić wartość "Opcjonalnie". Dlaczego ten przypadek użycia byłby nieważny? –

Odpowiedz

20

Mówiąc jako programista Guava, pozwól mi spróbować rozpakować logikę tutaj. Odpowiadając zarówno na oryginalne pytanie, jak i na wątek komentarza bezpośrednio na pytanie:

To jest absolutnie przypadek, że staramy się zmusić użytkowników Guava do przestrzegania naszych standardów dobrych nawyków programistycznych. (Nasze standardy są silnie wpływem np Effective Java.)

To powiedziawszy, zgadzam się, że istnieją przypadki użycia doskonale dobre dla zachowania masz na myśli w tym konkretnym pytaniem: „czy nieobecne, wyjątek . " Być może implementujesz klasę, do której można uzyskać dostęp w obie strony - jedną metodę z Opcjonalną wartością zwracaną i jedną metodę, która zakłada, że ​​wartość będzie zawsze obecna, rzucając wyjątek inaczej. Na przykład interfejs Deque oferuje wersje o specjalnej wartości i rzucające wyjątki, peek, poll i offer.

Wszystkie powiedział, że najlepiej, moim rozumieniu, prawdziwy Guava Droga do tego jest ...

if (value.isPresent()) { 
    return value.get(); 
} else { 
    throw new MyException(); 
} 

W „orThrow” metoda proponujesz wymaga refleksji (!!), nie pozwala dostosować wyjątek za pomocą użytecznego komunikatu itp. "Normalny sposób" jest doskonale czytelny i bardziej wydajny.

Czasami Guava nie zapewnia wyraźnego wsparcia dla rzeczy, ponieważ w przypadku tych przypadków uważamy, że najlepiej jest zrobić "normalny" sposób. Myślę, że tak jest w tym przypadku.

+3

Cóż, metoda 'orThrow()' nie wymaga specjalnie refleksji: można pomyśleć o delegowaniu instancji "Wyjątku" na "Dostawcę" lub "Funkcję". W każdym razie głównym punktem jest to, że "True Guava Way to doing" powtarza się około 50 razy z około 10 różnymi wyjątkami, i tylko z nowym kodem produkowanym od Guava 10! Nie chcemy powtarzać tego rodzaju kodu raz za razem. Stworzyliśmy kilka metod pomocniczych (1 na konstruktor 'Exception' + dodatki) i to nam wystarczy. Chciałem tylko wiedzieć, czy istnieje alternatywa, której nie ma. Nie otworzę biletu. –

+0

Dostawca lub funkcja wymagałaby użycia icky anonimowych klas, a jeszcze więcej kodu, niż jest to wymagane w prostym podejściu. Jeśli masz tyle powtórzeń, może to być odpowiednie, chociaż wydaje mi się, że najskuteczniejsze metody pomocnicze byłyby jednym pomocnikiem dla każdego typu wyjątku, który wydaje się być tym, co zrobiłeś. –

2

Nie sądzę, że należałoby to do biblioteki. Bardzo rzadko zdarza mi się znaleźć bibliotekę, która otrzymuje wystąpienie wyjątku do rzucenia na wypadek, gdyby coś nie poszło zgodnie z oczekiwaniami, tym bardziej, że w wielu przypadkach wyjątek musi zawierać komunikat wskazujący, co poszło nie tak.

Mimo to można utworzyć własną klasę opcjonalną, która spełnia wszystkie wymagania. Można też tworzyć własne klasy OptionalHelper gdzie masz metodę, która robi to, co chcesz:

public class OptionalHelper { 
    public <T> T valueOrThrow(final Optional<T> optional) throws MyException { 
     if (optional.isPresent()) { 
      return optional.get(); 
     } else { 
      throw new MyException(); 
     } 
    } 
} 

EDIT:

Przypuśćmy, że masz klasę niestandardową, która otrzyma nazwę parametru/pola, które trzeba sprawdzić , można mieć lepsze podejście podobne do tego, co robi Warunki:

public class OptionalHelper { 
    public <T> T valueOrFail(final Optional<T> optional, final String fieldName) throws OptionalNotPresentError { 
     if (optional.isPresent()) { 
      return optional.get(); 
     } else { 
      throw new OptionalNotPresentError(fieldName); 
     } 
    } 
} 
+0

Jeśli przekażesz własną klasę wyjątków, będziesz musiał ją utworzyć za pomocą Class.newInstance() przed jej wyrzuceniem. –

+0

Jak można zadeklarować to .newInstance'd wyjątek w klauzuli throws? Musiałbyś mieć ogólną podklasę Exception, o której wiesz, że zawiera wyjątek, który chcesz rzucić, prawda? To dla mnie nieprzyjemne. –

+0

Na pewno jest to niechlujne podejście. Dlatego nie mają go w bibliotece i dlatego nie uwzględniłem go w moim przykładzie (dodano go tylko jako komentarz). Odpowiadając na twoje pytanie, możesz uciec z deklarowaniem go, jeśli rozszerzysz swoją klasę wyjątków z RuntimeException (a nie z wyjątku). –

11

Oto kolejny sposób to zrobić bez dodatków do Guava:

Object result = optional.or(new Supplier() { 
    public Object get() { 
     throw new MyException(); 
    } 
}); 

MyException musi być odznaczony, ale to pozwala przekazywać argumenty do swojego konstruktora. Oczywiście, jeśli robisz to dużo, możesz gdzieś przechowywać Dostawcę i używać go w każdym miejscu, w którym go potrzebujesz.

Object result = optional.or(SomethingIsMissing.INSTANCE); 
1

Działa to dla mnie (bez refleksji, wystarczy wpisać wnioskowanie):

public class ExceptionSupplier<T, E extends RuntimeException> implements Supplier<T> { 

    private final E exception; 

    private ExceptionSupplier(E exception) { 
     this.exception = exception; 
    } 

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(E exception) { 
     return new ExceptionSupplier<T, E>(exception); 
    }  

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(@SuppressWarnings("UnusedParameters") Class<T> class_, E exception) { 
     return new ExceptionSupplier<T, E>(exception); 
    } 

    @Override 
    public T get() { 
     throw exception; 
    } 
} 

Zastosowanie:

Something something = optionalSomething.or(throwA(Something.class, new SomeException())); 

To prawdopodobnie może być przedłużony nawet dalej, ale dla moich przypadków użycia jest to wystarczające i łatwy rozumieć.

11

Nie ma nic wartościowego, że Java 8's Optional ma metodę orElseThrow, która pozwala na żądane zachowanie.

0

Zobacz official issue here

decyzję: NIE - zbyt drogie, a nie wspólny wzór, można po prostu użyć isPresent(), rzucać

Od optional.orThrow(new Exception()) nie jest dobre dla wydajności, wolę import statyczny, który jest podobny do @timk 's answer.

Result result = optional.or(throwException()); 

Równe

Result result = optional.or(SomeSupplier.throwException()); 

metoda statyczna

public static Supplier<Result> throwException() { 
    return new Supplier<Result>() { 
     @Override 
     public Result get() { 
      throw new RuntimeException(); 
     } 

    }; 
} 

===========

Ślad stosu wygląda

Exception ... RuntimeException 
at SomeSupplier$1.get(SomeSupplier.java:line where throw RuntimeException) 
at SomeSupplier$1.get(SomeSupplier.java:1) 
at com.google.common.base.Absent.or(Absent.java:60) 
at line where call optional.or(throwException()); 
+0

Jak wygląda ślad stosu? Jak działa śledzenie problemu, gdy widzisz to w dzienniku? – Ray

+1

@Ray Myślę, że jest to wzorzec, który rzuca określony wyjątek, a następnie gdzieś go obsłuży. Co więcej, dziennik można zrozumieć. – Anderson

+0

Dzięki za dodanie. Nie byłem pewny, w jaki sposób anonimowa klasa angażuje się w śledzenie stosów i myślę, że jest to istotne w dyskusji – Ray