============================================= Atributos y variables, estrucutras de control ============================================= Esta es la terera parte de este tour por Ceylon. En la pasada parada hemos aprendido acerca de clases y los conceptos de un atributo. Una de las cosas que hace una clase especial es que puede estados-referencia a otros objetos. Asi que e tiempo de aprender mas acerca de atributos y variables. Tambien veremos un poco de las estructuras de control (if, switch, for, while, y try). --------------------------- Atributos y valores locales --------------------------- En Java, un field de una clase es facilmente distinguir entre una variable local o parametro de un constructor. Esta distincion es menos significativa en Ceylon e frecuente irrelevante. Un atributo es realmente solo una valor declarado en la lista de parametros o en el cuerpo de la clase, puediendo ser capturado por alguna declaración `shared`. Aqui, `cuenta` es una variable local-block del inicializador de `Contador`. .. code-block:: ceylon class Contador () { variable Integer cuenta=0; } Pero en los siguientes dos ejemplos, `cuenta` es un atributo. .. code-block:: ceylon class Contador() { shared varible Integer cuenta=0; } class Contador(){ variable Integer cuenta=0; variable Integer inc() => ++cuenta++; } Esto puede ser un poco confuso la primera vez, pero es realmente como el principlo de trabajo de la closure. El mismo comportamiento aplica a valores local-block en el cuerpo de una funcion. Funciones no pueden ser declarados miembros shared, por su puesto, pero ellos pueden retornar un `objeto` que capture una varaible local: .. code-block:: ceylon interface Contador { shared formal Integer inc(); } Contador crearContador () { variable Integer cuenta=0; object contador satisfies Contador { shared actual Integer inc() => ++cuenta; } return contador; } O, como veremos mas adelante, una función puede retornar una funcion anidada que capture la variable local: .. code-block:: ceylon Integer() contador { variable Integer cuenta=0; Integer inc() => ++cuenta; return inc; } (No te preocupes mucho acerca de la sintaxis aqui - por ahora todo lo que nos interesa es en que `contador()` devueve una referencía a una función `inc()` la cual captura la variable `cuenta`.) Asi que aunque continuemos usando el termino "valor local" y "atributo" a lo largo de nuestro viaje, manten en mente que no hay una dinstinción significativa entre los terminos. Cualquier valor con nombre puede ser capturado por alguna otra declaración en el mismo contenido scope. Un valor local es solo un atributo que parece no ser capturado por nadie. --------- Variables --------- Ceylon alienta a usar atributos inmutables tanto como sea posible. Un atributo inmutable obtiene su valor cuando el objeto es inicializado, nunca mas puede ser reasignado. .. code-block:: ceylon class Referencia(val){ shared Valor val; } value ref = Referencia("foo"); print(ref.val); ref.val = "bar"; // compile error: value is no variable Si nostros queremos asignar un valor a la referencia necesitamos anotarla con `variable`: .. code-block:: ceylon class Referencia(val){ shared variable Valor val; } value ref = Referencia("foo"); print(ref.val); ref.val = "bar"; // ok print(ref.val); ------- Setters ------- Hemos conocido el concepto de un getter. Si queremos crear un atributo con un getter mutable, necesitamos definir un matching setter. Usualmete esto unicamente usable si tu tienes algun otro atributo interno que quieras al que quieras establecer un valor indirectamente. Supongamos nuestra clase tiene los siguientes atributos, diseñados para unicamente ser usados internamente (un-shared). .. code-block:: ceylon variable String? nombre=null; variable String? apellido=null; (Recuerda que ceylon nunca incializa los atributos automaticamente a null.) Entonces nosotros podemos abstraer los atributos usando un tercer atributo definido como un par getter/setter: .. code-block:: ceylon shared String nombreCompleto => " ".join(coalesce {nombre, apellido}); assign nombreCompleto { value tokens = nombreCompleto.split().iterator(); if (is String primero = tokens.next()){ nombre=primero; } if (is String ultimo=tokens.next()){ apellido=ultimo; } } Un setter es identificado por la palabra reservada `assign` en lugar del tipo de la declaración. (EL tipo de el matching getter determina el tipo de el atributo.) Dentro de el cuerpo de el setter, el atributo nombre evalua al valor a ser establecido Asi es, se parece mucho a un par de metodos get/set en Java, aunque la sintaxis es significativamente mas simplificada. Pero desde que los atributos en Ceylon son poliformicos, y desde que puedes redefinir una referencia como un getter o un par getter/setter sin afectar a los clientes que llamen al atributo, no necesitas definir getter y setters amenos que estes haciendo algo especial con el valor que estas obteniendo o estableciendo. Nunca escribas codigo como este en Ceylon: .. code-block:: ceylon variable String _name = " "; shared String name => _name; //pointless getter assign name => _name=name; //pointless setter No es necesario, y no obtines ningun beneficio de el. ---------------------- Estructuras de control ---------------------- Ceylon tiene seis estructuras de control que vienen integradas. No hay muy nuevo para los desarrolladores de Java o de C#, asi que unos pequeños ejemplos sin muchos comentarios adicionales deberas de ser suficientes. En primer lugar, un "gotcha" para los chicos que vienen de lenguajes similares a C: Ceylon no permite omitir las llaves en una estructura de control. El siguiente codigo niquiera sera parseado: .. code-block:: ceylon if (x>100) print("grande"); //error Se debe se escribir: .. code-block:: ceylon if (x>100) { print("big"); } La razón para que las llaves no sean opcionales en Ceylon es debido a que una expresion puede comenzar con una llave abierta, por ejemplo, `{"hola", "mundo" }`, así que llaves opcionales en estructuras de control harán que la gramática sea ambigua a el parser.) Ok, ahora es momento de ir a los ejemplos. ## if ## La declaración del `if/else` es completamente tradicional: .. code-block:: ceylon if (x > 1000) { print("Realmente grande"); } else if (x > 100) { print("Grande"); } else { print("pequeño"); } Después aprenderemos como el `if` puede estrechar el tipo de referencia en su bloque. Ya hemos visto un ejemplo de esto, pero volveremos a hablar de esto con cuando lleguemos a `tipos opcionales`. Nosotros usamos frecuentemente el operador `then` e `else` en vez de `if`. ###### switch ###### La declaración `switch/case` elimina el muy criticado comportamiento "fall through" y la sintaxis irregular: .. code-block:: ceylon switch (x<=>100) case (pequenio) { print("pequeño"); } case (igual) { print("Cien"); } case (grande) { print("Grande"); } EL tipo de la expresión que evalua switch deberá de ser un tipo `enumerated`. Tu no puedes usar switch con un `String` o un `Integer`. (Utiliza `if` en vez de ello.) Aun tenemos mucho mas que decir acerca de `switch` cuando discutamos tipos `enumerated`. ###### assert ###### Ceylon también tiene una declaración `assert`: .. code-block:: ceylon assert (longitud < 10); Los `assert` son buenos para crear declaraciones donde tu conoces que tiene que ser verdadero, pero no son aparentes a otros lectores del código(incluyendo el identificador de tipos!). Los usos comunes de un `assert` incluyen cosas como precondiciones, postcondiciones y clases invarientes. Si la condición es `false` es lanzada una excepciones en tiempo de ejecución. El mensaje de la excepción ayudara incluira detalles de la condición que fue violada, esto es muy importante cuando un `assert` tiene mas de una condicional. .. code-block:: ceylon assert (exists arg, !arg.empty); Para personalizar el mensaje de `assert`, agrega una anotación `doc`: .. code-block:: ceylon "La longitud debera ser de almenos 10" assert (longitud < 10) En su caso, el analizador de tipos usara `assert` información para los tipos cuando checa declaraciones que siguen al `assert`, por ejemplo: .. code-block:: ceylon Integer? x = parseInteger("1"); assert (exists x); // Despues de `assert`, x tiene el tipo Integer enve de Integer? value y = x+10; Esto es realmente el mismo comportamiento que hemos visto anteriormente, unicamente esta vez enmedio de un bloque en vez de al inicio de un bloque `if`. (No te preocupes, habrá más de estos ejemplos después.) Nótese que, a diferencia del `assert` de Java, que puede ser deshabilitado en tiempo de ejecución, los `assert` en Ceylon siempre estarán habilitados. ### for ### EL ciclo `for` tiene un bloque opcional `else`, que puede ser ejecutado cuando el ciclo termina completamente sus iteraciones si pasar por una declaración `return` o un `break`. .. code-block:: ceylon variable Boolean menores; for (p in gente) { if (p.age<18) { menores=true; break; } } else { menores=false; } Este no es un `for` estilo C. En vez de ello, puedes usar el operador de rango longitudinal `:`, para producir una secuencia de `Integer`, dando su inicio y su longitud: .. code-block:: ceylon for (i in min:lon) { ... } Alternativamente, puede usar el operador de rango común `..` para producir una secuencia de `Integer` entre dos puntos: .. code-block:: ceylon for (i in min..max) { ... } Existen algunos otros trucos con un `for` que veremos mas adelante. Nosotros usamos `comprehensions` o incluso funciones `higher order` en vez de `for`. ##### while ##### EL `while` puede se usado de la forma tradicional. .. code-block:: ceylon value it = nombres.iterator(); while (is String siguiente = it.next()){ print(siguiente); } No hay una declaración `do/while`. ### try ### La declaración `try/catch/finally` trabajan como en Java: .. code-block:: ceylon try { message.send(); } catch (ConnectionException|MessageException e) { tx.setRollbackOnly(); } Para manejar todas la excepciones de Ceylon, junto con todas las excepciones de JavaScript, o todas las excepciones que son subclases de java.lang.Exception, , podemos `catch` el tipo `Exception` definido en `ceylon.language`. Si no especificamos explicitamente un tipo, `Exception` es inferido: .. code-block:: ceylon try { message.send(); } catch (e) {// Equivalente a "catch (Exception e)" tx.setRollbackOnly(); } No hay una manera de manejar excepciones de tipo `java.lang.Error`. Eventualmente `try` soportara expresiones `resource` similares a las Java 7. .. code-block:: ceylon try (Transaction()) { try(s = Session()){ s.persist(person); } } **Notas de la implementación Milestone 5** Expresiones `Resource` aun no son implementados. ############## Condition list ############## Construcciones como `if,while` y `assert` aceptan `Contion list`. Una `condition list` es una lista no ordenada de múltiples booleanos, `exists, nonempty` e `is. La `condition list` es satisfecha si (y solo si) cada una de las condicionales es satisfecha. Con condicionales booleanas puedes lograr el mismo comportamiento con el operador &&. Pero con `condition list` te permite usar el "structured typecasting" de `exists, is` y amigos en condiciones que aparezcan después en la misma lista. Veamos un ejemplo usando `assert`: .. code-block:: ceylon value url = parseUri("http://ceylon-lang.org/download"); assert(exists authority=url.authority, exists host=authority.hostname); // Hacer algo con host Aqui puedes ver dos condiciones `exists` en la declaración `assert`, separadas con coma. La primera declara `authority`(que es inferida a ser una `String` en vez de una `String?` debido al `exists`). La segunda condición entonces usa su propia `exists` condición. Lo importante a notar es que el compilador nos permite usar `authority` en la segunda condición y conocer que es una `String` y no una `String?`. No puedes hacer esto con múltiples "&&" condiciones. Puedes lograrlo anidando varios `if`, pero hará menos legible el código y no trabajara bien en un declaración `while` o `somprehension`. ############## Aun hay mas... ############## Ahora que conocemos acerca de las clases y sus miembros, estamos listos para explorar herencia y refinamiento(overriding).