2016-01-18 16 views
28

La variabile utilizzata nell'espressione lambda deve essere definitiva o effettivamente definitiva. Quando provo a utilizzare calTz, viene visualizzato questo errore.utilizzata nell'espressione lambda deve essere definitiva o efficace finale

+2

Non è possibile modificare 'calTz' dal lambda. –

+0

Dai un'occhiata [questo eccellente post di Bruce Eckel] (http://bruceeckel.github.io/2015/10/17/are-java-8-lambdas-closures/) su come Java 8 Lambdas è diverso da (Python) Chiusure e perché è possibile passare solo ** valori ** anziché ** variabili **. –

risposta

21

Una variabile final significa che può essere istanziata solo una volta. in Java non è possibile utilizzare variabili non finali in lambda e nelle classi interne anonime.

È possibile refactoring del codice con il vecchio per-ogni loop:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { 
    try { 
     for(Component component : cal.getComponents().getComponents("VTIMEZONE")) { 
     VTimeZone v = (VTimeZone) component; 
      v.getTimeZoneId(); 
      if(calTz==null) { 
       calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); 
      } 
     } 
    } catch (Exception e) { 
     log.warn("Unable to determine ical timezone", e); 
    } 
    return null; 
} 

Anche se non ottengo il senso di alcuni pezzi di questo codice:

  • si chiama un v.getTimeZoneId(); senza utilizzare il suo valore di ritorno
  • con l'assegnazione calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); non si modifica l'origine passato calTz e non si utilizza in questo metodo
  • Si restituisce sempre null, perché non si imposta void come tipo di reso?

Spero anche che questi suggerimenti ti aiutino a migliorare.

14

Da un lambda, non è possibile ottenere un riferimento a tutto ciò che non è definitivo. Devi dichiarare un wrapper finale al di fuori della lama per mantenere la tua variabile.

Ho aggiunto l'oggetto "di riferimento" finale come questo wrapper.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { 
    final AtomicReference<TimeZone> reference = new AtomicReference<>(); 

    try { 
     cal.getComponents().getComponents("VTIMEZONE").forEach(component->{ 
     VTimeZone v = (VTimeZone) component; 
      v.getTimeZoneId(); 
      if(reference.get()==null) { 
       reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue())); 
      } 
      }); 
    } catch (Exception e) { 
     //log.warn("Unable to determine ical timezone", e); 
    } 
    return reference.get(); 
} 
+0

Stavo pensando allo stesso approccio o simile - ma mi piacerebbe vedere alcuni consigli di esperti/feedback su questa risposta? – YoYo

3

Nel tuo esempio, è possibile sostituire il forEach con Lamdba con un semplice for ciclo e modificare qualsiasi variabile liberamente. O, probabilmente, refactoring il codice in modo che non è necessario modificare le variabili. Tuttavia, spiegherò per completezza cosa significa l'errore e come aggirare il problema.

Java 8 Language Specification, §15.27.2:

Qualsiasi variabile locale, parametro formale, o il parametro eccezioni utilizzato ma non dichiarata in un'espressione lambda deve o essere dichiarata definitiva o essere efficacemente finale (§4.12.4), o un si verifica un errore in fase di compilazione in cui viene tentato l'utilizzo.

Fondamentalmente non è possibile modificare una variabile locale (calTz in questo caso) da una lambda (o una classe locale/anonima). Per ottenere ciò in Java, devi usare un oggetto mutabile e modificarlo (tramite una variabile finale) dal lambda. Un esempio di un oggetto mutabile qui sarebbe un array di un elemento:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) { 
    TimeZone[] result = { null }; 
    try { 
     cal.getComponents().getComponents("VTIMEZONE").forEach(component -> { 
      ... 
      result[0] = ...; 
      ... 
     } 
    } catch (Exception e) { 
     log.warn("Unable to determine ical timezone", e); 
    } 
    return result[0]; 
} 
12

Java 8 ha un nuovo concetto chiamato variabile "Efficacemente finale". Significa che una variabile locale non finale il cui valore non cambia mai dopo l'inizializzazione è chiamata "Effettivamente finale". Questo concetto è stato introdotto perché prima di Java 8 non era possibile utilizzare una variabile locale non finale in una classe anonima. Se hai accesso a una variabile locale nella classe Anonymous, devi renderla definitiva. Quando Lambdas è stato introdotto, questa restrizione è stata attenuata. Quindi alla necessità di rendere locale varibale finale se non viene modificato una volta che è inizializzato come Lambda in sé non è altro che una classe anonima.Java 8 ha realizzato il dolore di dichiarare la variabile locale finale ogni volta che lo sviluppatore ha utilizzato Lambda e ha introdotto questo concetto e ha reso superfluo rendere definitive le variabili locali. Quindi, se vedi che la regola per la classe anonima non è ancora cambiata, è solo che non devi scrivere la parola chiave finale ogni volta che usi lambdas.

ho trovato una buona spiegazione here

0

se non è necessario modificare la variabile di una soluzione per questo tipo di problema sarebbe quello di estrarre la parte di codice che utilizzano lambda e utilizzare parola chiave final sul parametro-metodo.

Problemi correlati