2010-10-26 13 views
22

Perché non è possibile avere questo:Scala vals assegnazione

def main(args:Array[String]) { 
    val whatever:String // Have it uninitialized here 

    if(someCondition) { 
     whatever = "final value" // Initialize it here 
    } 
} 

Non capisco perché questo non dovrebbe essere legale. So che posso renderlo un var, ma perché dobbiamo inizializzare lo val esattamente quando lo dichiariamo? Non sembra più logico essere in grado di inizializzarlo in seguito?

+0

Come fa il compilatore a verificare che sia effettivamente inizializzato? Sì, è possibile, ma difficile e non necessario. – delnan

+2

Perché fino al punto di quell'inizializzazione, il valore è * non definito * e ha solo senso in termini di un flusso di controllo successivo. Nella programmazione funzionale, non ci sono valori indefiniti - e valori - non stati - sono le uniche cose che contano. – Dario

+0

Beh, in pratica non esiste "non inizializzato". La JVM non supporta questo concetto. In Java questi campi "non inizializzati" hanno il valore 'null'. A causa del modo in cui funziona l'inizializzazione della classe, a volte è possibile accedere ai campi e recuperare i null bogus. Ciò porta a un codice che sembra insospettabile, ma genera 'NPE' in fase di runtime. Serializzazione/RMI aggiunge più problemi a tale approccio. Se sei interessato, guarda alcuni esempi in "Java Puzzlers". Fondamentalmente: Scala impedisce che accadano cose strane e riduce la necessità di valori non inizializzati, perché tutto è un'espressione. – soc

risposta

38

Si può fare:

val whatever = 
    if (someCondition) 
     "final value" 
    else 
     "other value" 
+1

E se alcuneCondizioni devono essere utilizzate per impostare più di un valore, è possibile utilizzare le tuple e la decomposizione del valore. – andresp

3

Poiché lo scopo di 'val' è quello di segnalare al lettore (e il compilatore): "Questo valore rimarrà quello che è inizializzato a fino a che non passa mai di esistenza "

Questo non ha molto senso senza inizializzazione.

Ovviamente uno potrebbe sognare qualcosa come val (3) che consente tre assegnazioni a una variabile, ma non penso che sarebbe di grande utilità.

9

Usa lazy val s in questo modo:

def main(args:Array[String]) { 
    lazy val whatever:String = if (someCondition) "final value" else "other value" 

    // ... 

    println(whatever) // will only initialize on first access 
} 
+1

Il compito all'interno dell'unità restituirà l'unità. Un if senza un'altra clausola restituirà anche l'unità. Penso che l'esempio non verrà compilato ... –

+0

Leggi prima di inviare ... Hai ragione. Corretto. –

7

In aggiunta a ciò che altri hanno detto, notare che Java consente "vuoti finali" "variabili", che è una caratteristica ho un pò manca:

final Boolean isItChristmasYet; 

if (new Date().before(christmas)) { 
    isItChristmasYet = Boolean.FALSE; 
} else { 
    isItChristmasYet = Boolean.TRUE; 
} 

Tuttavia, grazie all'analisi del flusso di dati nel compilatore, javac non consente di lasciare la variabile whatever non assegnata se someCondition non viene mantenuto.

soluzione
+0

Sì, questo è quello che mi manca anche da Java. – Geo

+1

Per curiosità, che cosa guadagni esattamente avendo la variabile non inizializzata pre-dichiarata? Non è come se potessi usarlo in qualsiasi modo prima che sia effettivamente inizializzato ... –

+0

È una cosa stilistica, ho usato per dichiarare tutti i miei locals in prima linea nella parte superiore del loro scopo. Probabilmente lo farei, almeno a volte, se Scala avesse dei valori vuoti. :) –

27

Java è in realtà una soluzione al problema che non tutte le espressioni valori di ritorno, quindi non è possibile scrivere questo in Java:

final String whatever = if (someCondition) { 
    "final value" 
} else { 
    "other value" 
} 

Sempre più spesso, la tendenza in Java è quello di usare l'operatore ternario invece:

final String whatever = someCondition ? "final value" : "other value" 

che va bene per questo caso un uso limitato, ma del tutto insostenibile, una volta che si avvia trattare con istruzioni switch e più costruttori.


L'approccio di Scala è diverso qui. Tutta la costruzione dell'oggetto deve passare attraverso un singolo costruttore "primario", tutte le espressioni restituiscono un valore (anche se è Unit, equivalente a Java Void) e l'iniezione del costruttore è fortemente favorita. Ciò comporta che i grafici degli oggetti siano costruiti in modo chiaro come un grafico aciclico diretto, e funziona anche molto bene con oggetti immutabili.

Si desidera inoltre essere consapevoli del fatto che dichiarare e definire variabili in operazioni separate è, in generale, una cattiva pratica quando si ha a che fare con più thread - e potrebbe lasciarti vulnerabile all'esposizione di null e condizioni di gara quando meno te lo aspetti (anche se questo non è veramente un problema durante la costruzione dell'oggetto). La creazione atomica di valori immutabili è solo un aspetto del modo in cui i linguaggi funzionali aiutano a gestire la concorrenza.

Significa anche che il compilatore Scala può evitare alcune delle analisi del flusso orribilmente complicate dalle specifiche del linguaggio Java.

Come detto in precedenza, la Scala Way ™ è:

val whatever = 
    if (someCondition) 
    "final value" 
    else 
    "other value" 

Un approccio che scala anche ad altre strutture di controllo:

val whatever = someCondition match { 
    case 1 => "one" 
    case 2 => "two" 
    case 3 => "three" 
    case _ => "other" 
} 

Con un po 'di esperienza Scala scoprirete che questo stile aiuta il compilatore ad aiutarti e dovresti trovarti a scrivere programmi con meno bug!