2010-10-26 6 views
22

Por qué no es posible tener presente:Scala asignación de Vals

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

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

No entiendo por qué esto no debería ser legal. Sé que puedo convertirlo en var, pero ¿por qué tenemos que inicializar el val exactamente cuando lo declaramos? ¿No parece más lógico poder inicializarlo más tarde?

+0

¿Cómo se aseguraría el compilador de que se haya inicializado? Sí, es posible, pero difícil y no es necesario. – delnan

+2

Porque hasta el momento de esa inicialización, el valor es * undefined * y simplemente tiene sentido en términos de un flujo de control siguiente. En la programación funcional, no hay valores indefinidos, y los valores, no los estados, son lo único que importa. – Dario

+0

Bueno, básicamente no hay "sin inicializar". La JVM no es compatible con este concepto. En Java, estos campos "no inicializados" tienen el valor 'null'. Debido a la forma en que funciona la inicialización de clase, a veces es posible acceder a los campos y obtener nulos falsos. Esto lleva a un código que parece no sospechoso, pero arroja 'NPE's en tiempo de ejecución. La serialización/RMI agrega más problemas a ese enfoque. Si estás interesado, mira algunos ejemplos en "Puzzlers de Java". Básicamente: Scala evita que ocurran cosas extrañas y reduce la necesidad de valores no inicializados, porque todo es una expresión. – soc

Respuesta

38

que puede hacer:

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

Y si se necesita usar alguna condición para establecer más de un valor, puede usar tuplas y descomponer el valor. – andresp

3

Debido a que el propósito de 'val' es señalarle al lector (y el compilador): "Este valor se quedará lo que se inicializa a hasta que sale de existencia "

Esto no tiene mucho sentido sin la inicialización.

Por supuesto, uno podría soñar con algo como val (3) que permite la asignación de tres a una variable, pero no creo que sea de mucha utilidad.

9

Uso lazy val s de este 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

La asignación dentro de if devolverá la unidad. Una cláusula if without an else también devolverá la unidad. Creo que tu ejemplo no compilará ... –

+0

Leer antes de enviar ... Tienes razón. Corregido –

7

Además de lo que otros han dicho, tenga en cuenta que Java permite "finales en blanco" "variables", que es una característica que un poco extraño:

final Boolean isItChristmasYet; 

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

Sin embargo, gracias al análisis de flujo de datos en el compilador, javac no le permitirá dejar su variable whatever sin asignar si someCondition no se mantiene.

solución
+0

Sí, esto es lo que extraño de Java también. – Geo

+1

Por curiosidad, ¿qué gana exactamente al pre-declarar la variable no inicializada? No es como si pudieras usarlo de ninguna manera antes de que se haya inicializado realmente ... –

+0

Es una cosa estilística, yo solía declarar a todos mis locales por adelantado en la parte superior de su alcance. Probablemente todavía lo haría, al menos algunas veces, si Scala tuviera vals en blanco. :) –

27

El Java es en realidad una solución para el problema de que no todas las expresiones devuelven valores, por lo que no se puede escribir esto en Java:

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

Cada vez más, la tendencia en Java es utilizar el operador ternario en su lugar:

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

Lo que está bien para ese caso de uso limitado, pero totalmente insostenible una vez que empezar a tratar con los estados de conmutación y varios constructores.


El enfoque de Scala es diferente aquí. Toda construcción de objeto debe pasar finalmente a través de un único constructor "primario", todas las expresiones devuelven un valor (incluso si es Unit, equivalente a Void de Java), y la inyección del constructor es muy favorable. Esto da como resultado gráficos de objetos que se construyen de forma limpia como un Gráfico Acíclico Dirigido, y también funciona muy bien con objetos inmutables.

También debe tener en cuenta que declarar y definir variables en operaciones separadas es, en general, una mala práctica al tratar con múltiples hilos, y podría dejarlo vulnerable a exponer nulos y condiciones de carrera cuando menos lo espere (aunque esto no es realmente un problema durante la construcción del objeto). La creación atómica de valores inmutables es solo un aspecto de la forma en que los lenguajes funcionales ayudan a lidiar con la concurrencia.

También significa que el compilador de Scala puede evitar parte del análisis de flujo horriblemente complicado de la especificación de lenguaje de Java.

Como se ha indicado anteriormente, la Scala Way ™ es:

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

Un enfoque que también se amplía hasta otras estructuras de control:

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

Con un poco de experiencia Scala descubrirá que este estilo ayuda al compilador a ayudarte, ¡y deberías escribir programas con menos errores!

Cuestiones relacionadas