2016-06-09 21 views
16

Ho eseguito l'aggiornamento a Java 8 e ho provato a sostituire una semplice iterazione tramite una mappa con una nuova espressione lamdba. Il ciclo ricerca valori nulli e genera un'eccezione se ne viene trovato uno. Il vecchio codice Java 7 è simile al seguente:Perché non posso lanciare un'eccezione in un'espressione lambda Java 8?

for (Map.Entry<String, String> entry : myMap.entrySet()) { 
    if(entry.getValue() == null) { 
     throw new MyException("Key '" + entry.getKey() + "' not found!"); 
    } 
} 

E il mio tentativo di convertire questo in Java 8 assomiglia a questo:

myMap.forEach((k,v) -> { 
    if(v == null) { 
     // OK 
     System.out.println("Key '" + k+ "' not found!"); 

     // NOK! Unhandled exception type! 
     throw new MyException("Key '" + k + "' not found!"); 
    } 
}); 

Qualcuno può spiegare perché la dichiarazione throw non è consentita qui e come questo potrebbe essere corretto? quick-fix suggerimento

di Eclipse non guardare a destra a me ... circonda semplicemente la dichiarazione throw con un try-catch blocco:

myMap.forEach((k,v) -> { 
    if(v == null) { 
     try { 
      throw new MyException("Key '" + k + "' not found!"); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
}); 
+12

C'è una risposta molto semplice: le espressioni lambda sono un mezzo concisa di attuazione di un'interfaccia funzionale. Come qualsiasi altro modo per implementare un'interfaccia, devi conformarti al contratto dell'interfaccia, il che significa che puoi solo lanciare le eccezioni consentite dal tipo di target. Non c'è magia qui. –

risposta

29

Non ti è permesso di lanciare eccezioni controllate perché il metodo void accept(T t, U u) nel L'interfaccia BiConsumer<T, U> non genera eccezioni. E, come sai, forEach prende questo tipo.

public void forEach(BiConsumer<? super K, ? super V> action) { ... } 
         | 
         V 
@FunctionalInterface 
public interface BiConsumer<T, U> { 
    void accept(T t, U u); // <-- does throw nothing 
} 

Questo è vero quando si tratta di eccezioni controllate. Ma v'è permesso di un'eccezione senza controllo, per esempio, un IllegalArgumentException:

new HashMap<String, String>() 
    .forEach((a, b) -> { throw new IllegalArgumentException(); }); 
+3

Sono praticamente d'accordo con questa risposta. La cosa confusa riguardo ai javadoc è la frase "Le eccezioni generate dall'azione vengono inoltrate al chiamante". Come nota a margine, il lancio di eccezioni all'interno di una lambda è certamente consentito, ma non in questo caso particolare. – micker

+0

stessa cosa che ho vissuto. l'eccezione controllata non è consentita. – lwpro2

+0

@micker Potrebbe valere la pena sottolineare che questa istruzione è collegata solo al metodo membro predefinito 'forEach' dell'interfaccia' Iterable '. Il flusso primario/consumer 'forEach' non documenta nulla sul comportamento delle eccezioni verificate (anche se alcune conclusioni possono essere tratte dalle firme delle funzioni). –

18

È possibile generare eccezioni in lambda.

Un lambda è autorizzato a generare le stesse eccezioni dell'interfaccia funzionale implementata dalla lambda.

Se il metodo dell'interfaccia funzionale non ha una clausola di tiri, il lambda non può lanciare CheckedExceptions. (È comunque possibile lanciare RuntimeExceptions).


Nel tuo caso particolare Map.forEach utilizza una BiConsumer come parametro, BiConsumer è definito come:

public interface BiConsumer<T, U> { 
    void accept(T t, U u); 
} 

un'espressione lambda per questa interfaccia funzionale non può buttare CheckedExceptions.


I metodi nelle interfacce funzionali definiti nel java.util.function pacchetto non gettare eccezioni, ma è possibile utilizzare altre interfacce funzionali o creare il proprio per essere in grado di generare eccezioni, vale a dire dato questa interfaccia:

public interface MyFunctionalInterface<T> { 
    void accept(T t) throws Exception; 
} 

Il seguente codice sarebbe legale:

MyFunctionalInterface<String> f = (s)->throw new Exception("Exception thrown in lambda");