2011-12-16 6 views
13

Continuo a scrivere specifici lanciatori di eccezioni nel caso in cui l'opzionale sia assente.Collegamento per l'uso facoltativo di Guava con eccezioni?

Per esempio:

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

Trovo che questo codice non molto fluente, in particolare l'uso del punto esclamativo (!). Preferirei come scrivere qualcosa di simile a:

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

Esiste un collegamento nella Guava che non ho ancora trovato?

+6

Domanda generale. Se la cosa che stai verificando è Opzionale, non è contrario al concetto di "facoltativo" avere un metodo come "Optional.orThrow"? –

+4

Totalmente d'accordo. Se 'isAbsent' (aggiunto nella versione 11) è una condizione valida, non dovrebbe comportare un'eccezione. Il punto di "Optional" è che l'assenza è un caso valido non eccezionale. Se assente è un caso eccezionale, il tuo metodo dovrebbe solo restituire l'oggetto e gettare ed eccezione nel caso assente. –

+2

panda nero: allora perché posso dare un valore predefinito con il metodo 'or()'? È un tipo di consentire un comportamento predefinito, perché non un altro? @ John B: uno dei casi d'uso è un estrattore di attributi XML piuttosto generico: quell'attributo può essere o non essere presente, quindi il metodo restituisce un 'Optional'. Quindi il chiamante potrebbe voler imporre la presenza del valore (lanciando un'eccezione), oppure ottenere il valore, se presente, o persino recuperare un valore predefinito. Il metodo estrattore semplicemente non sa cosa vuole fare il chiamante con il valore, quindi deve restituire un 'Optional'. Perché questo caso d'uso non sarebbe valido? –

risposta

20

Parlando come sviluppatore Guava, vorrei provare a decomprimere la logica qui. Rispondendo sia alla domanda originale, sia alla discussione del commento direttamente sulla domanda:

È assolutamente il caso che cerchiamo di costringere gli utenti di Guava a rispettare i nostri standard di buone abitudini di programmazione. (I nostri standard sono fortemente influenzato da esempio Effective Java.)

Detto questo, sono d'accordo che ci sono perfettamente buoni casi di utilizzo per il comportamento ti riferisci in questa particolare domanda: "se assente, un'eccezione ". Forse stai implementando una classe a cui è possibile accedere in entrambi i modi: un metodo con un valore di ritorno Opzionale e un metodo che presuppone che il valore sarà sempre presente, generando altrimenti un'eccezione. L'interfaccia di Deque, ad esempio, fornisce versioni a valore speciale e di eccezione per peek, poll e offerte.

Tutto ciò che ha detto, al meglio della mia comprensione, il Vero Guava modo per farlo è ...

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

Il metodo "orThrow" proponi richiede una riflessione (!!), non lo fa ti consente di personalizzare l'eccezione con un messaggio utile, ecc. Il "modo normale" è perfettamente leggibile e più efficiente.

A volte Guava non fornisce supporto esplicito per le cose, perché per quei casi d'uso, pensiamo che sia meglio fare solo il "modo normale". Penso che questo sia il caso qui.

+3

Bene, un metodo 'orThrow()' non richiede particolare riflessione: si può pensare di delegare l'instanciazione di 'Exception' a' Supplier' o a 'Function'. Ad ogni modo, il punto principale è che il "True Guava Way to do this" è ripetuto circa 50 volte con circa 10 diverse eccezioni, e solo con il nuovo codice prodotto da Guava 10! Non vogliamo duplicare questo tipo di codice ancora e ancora. Abbiamo creato diversi metodi di supporto (1 per costruttore di 'Exception' + extra) e questo è abbastanza per noi. Volevo solo sapere se esisteva un'alternativa, che non esiste. Non aprirò un biglietto. –

+0

Un fornitore o una funzione richiederebbero l'uso di classi icky anonimi e ancora più codice di quello richiesto nell'approccio diretto. Se si dispone di tale ripetizione potrebbe essere appropriato, anche se penserei che i metodi di supporto più efficaci sarebbero un aiuto per ogni tipo di eccezione, che sembra essere quello che hai fatto. –

2

Non penso che questo appartenga alla biblioteca. Trovo molto raro trovare una libreria che riceva un'istanza di un'eccezione da gettare nel caso in cui qualcosa non vada come previsto, soprattutto perché in molti casi un'eccezione deve avere un messaggio che indica cosa è andato storto.

Detto questo, è possibile creare la propria classe opzionale che fa ciò di cui si ha bisogno. Oppure si può creare la propria classe OptionalHelper in cui si dispone di un metodo che fa quello che si vuole:

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

EDIT:

Supponiamo di avere una classe personalizzata che riceve un nome di parametro/campo che è necessario controllare , si potrebbe avere un approccio migliore simile a quello che fa Presupposti:

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

Se si passa la propria classe di eccezione, sarà necessario creare un'istanza con Class.newInstance() prima di lanciarla. –

+0

Come dichiareresti questa eccezione .newInstance'd nella clausola throws? Dovresti avere una sottoclasse Exception generale che sai che contiene l'eccezione che vuoi lanciare, giusto? Mi sembra complicato. –

+0

È sicuramente un approccio disordinato. Questo è il motivo per cui non ce l'hanno nella libreria e questo è il motivo per cui non l'ho incluso nel mio esempio (l'ho solo aggiunto come commento). Rispondendo alla tua domanda, potresti farla franca dichiarandola se estendi la tua classe di eccezione da RuntimeException (e non da Exception). –

11

Ecco un altro modo per farlo senza aggiunte di Guava:

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

MyException deve essere deselezionato, ma ciò consente di passare argomenti al suo costruttore. E, naturalmente, se lo fai molto, puoi conservare il Fornitore da qualche parte e usarlo in ogni posto che ti serve.

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

questo funziona per me (nessuna riflessione, basta digitare l'inferenza):

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; 
    } 
} 

Usage:

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

Questo probabilmente può essere esteso ancora di più, ma per i miei casi d'uso è sufficiente e facile capire.

11

Non vale nulla che Java 8 di Optional abbia un metodo orElseThrow che consente il comportamento richiesto.

0

Vedi official issue here

Decisione: NO - troppo costoso, non un modello comune, può semplicemente utilizzare isPresent(), gettare

Dal optional.orThrow(new Exception()) non è buono per le prestazioni, preferisco importazione statica, che è simile a @timk 's answer.

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

Uguale a

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

Metodo statico

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

    }; 
} 

===========

L'analisi dello stack si presenta come

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

Qual è la traccia dello stack di questo aspetto? Come si fa a rintracciare un problema quando lo vedi in un log? – Ray

+1

@Ray Penso che sia uno schema che genera un'eccezione specifica, quindi da qualche parte lo gestirà. Inoltre, il registro può essere compreso. – Anderson

+0

Grazie per l'aggiunta. Non ero sicuro di come la classe anonima venga coinvolta nello stack trace, e penso sia rilevante per la discussione – Ray

Problemi correlati