Este es la segunda parada en nuestro tour por el lenguaje de programación Ceylon. En el capitulo anterior has aprendido la básico acerca de la sintaxis de Ceylon. En esta parada, aprenderemos como definir clases con métodos y atributos.
El caso del primer carácter de un identificador es importante. Los nombres de los tipos (interface, class, type parameter) deberán de comenzar la letra inicial con mayúscula. Los nombres de las funciones y valores comenzaran con minúscula o guión. El compilador de ceylon es muy exigente respecto a este aspecto, así que obtendrás un error de compilación.
class hola() { ... } //compile error
o
String Name = .... //compile error
Esta es una manera de sobrellevar las restricciones, la cual es muy útil cuando trabajamos cuando trabajamos con Java. Puedes “forzar” a el compilador a entender que un identificador es el nombre de un Tipo de dato pre fijándolo con I, o que es el nombre de una función o valor con el prefijo i. Por ejemplo, iRojo es considerado un identificador inicial en minúscula.
Las siguientes declaraciones son aceptables, pero definitivamente no son recomendadas, excepto en un escenario interoperativo.
class \Ihola() { ... } //compile error
o
String \iName = .... //compile error
Nuestra primera clase haremos que represente un punto en un sistema de coordenadas polares. Nuestra clase toma dos parámetros, dos métodos y un atributo.
class Polar (Float angulo, Float radio) {
shared Polar rotate(Float rotation) =>
Polar(angle + rotation, radius);
shared Polar dilate(Float dilation) =>
Polar(angle, radius*dilation);
shared String description = "(``radius``,``angle``)";
}
Debemos hacer notar dos cosas en particular en el código:
Nótese también que Ceylon no tienen una palabra reservada new para indicar la instanciación, solo se necesita escribir Polar(angulo, radio), para invocar a la clase.
La anotación shared determina la accesibilidad de el tipo de dato anotado, atributo o método. Antes de seguir avanzando, veamos como podemos ocultar la implementación interna de una clase a otros códigos.
Ceylon no hace dinstinciń entre public, protected y default visibilidad como java lo hace; Aqui el porque En vez de ello, el lenguaje distingue entre:
Por defecto, los miembros de la clase son ocultados para todos afuera de la definición de la clase. Por anotación, un miembro con la anotación shared, es declarado para ser visible a cualquier código donde la clase sea visible.
Así pues, una clase puede ser ocultada de otro código. Por defecto una clase toplevel son ocultadas hacia fuera del código donde la clase fue definida. Anotando a una clase top level la hace visible a cualquier código donde el paquete que la contiene es visible.
Finalmente, los paquete son ocultos del código fuera del modulo al que el paquete pertenece por default. Únicamente paquetes compartidas explícitamente son visibles a otros módulos.
¿Captas la idea? Hemos estado jugando muñecas rusas.
Si queremos mostrar el angulo y el radio de nuestra coordenada Polar a otro código, necesitamos definir los atributos de la clase. Es común asignar los parámetros de una clase directamente a un atributo shared de la clase, así que Ceylon nos provee de una sintaxis simplificada para este propósito.
"A polar coordinate"
class Polar(angle, radius) {
shared Float angle;
shared Float radius;
shared Polar rotate(Float rotation) =>
Polar(angle+rotation, radius);
shared Polar dilate(Float dilation) =>
Polar(angle, radius*dilation);
shared String description = "(``radius``,``angle``)";
}
Todo código que use Polar puede acceder a los atributos de la clase usando una sintaxis muy conveniente.
shared Cartesian cartesian(Polar polar) {
return Cartesian(polar.radius*cos(polar.angle),
polar.radius*sin(polar.angle));
}
Incluso existe una manera mas compacta de escribir el código anterior, aunque en un poco menos legible.
"A polar coordinate"
class Polar(shared Float angle, shared Float radius) {
shared Polar rotate(Float rotation) =>
Polar(angle+rotation, radius);
shared Polar dilate(Float dilation) =>
Polar(angle, radius*dilation);
shared String description = "(``radius``,``angle``)";
}
Esto ilustra una importante característica de Ceylon: No hay una suficiente diferencia esencial aparte de la sintaxis, entre parámetros de una clase y una valor declarado en el de una clase.
En vez de declarar los atributos en el cuerpo de una clase, simplemente podemos anotar el parámetro con shared. Nosotros recomendamos que evites esta sintaxis cuando tengas más de uno o dos parámetros.
Los atributos angulo y radio son referencias, y es lo mas cercano que tienen Ceylon a un field en Java. Usualmente declaramos el valor de una referencia cuando la declaramos.
shared Float x = radio * sin(angulo);
shared String Saludo = "Hola, ``nombre``";
shared Integer meses = anios * 12;
En algunas veces se tiene que separar la declaración de la asignación.
shared String descripcion;
if (exists etiqueta) {
descripcion = etiqueta;
}
else {
descripcion = "(``radio``,``angulo``)";
}
Pero si no existe un constructor en Ceylon, ¿dónde deberiamos de poner este código? Debemos de ponerlo dentro del cuerpo de la clase.
"Una cordenada polar con una etiqueta opcional"
class Polar(angulo, radio, String? etiqueta) {
shared Float angulo;
shared Float radio;
shared String descripcion;
if (exists etiqueta) {
descripcion = etiqueta;
}
else {
descripcion = "(``radio``,``angulo``)";
}
// ...
}
EL compilador de Ceylon te fuerza a especificar un valor de cualquier referencia antes hacer uso de la referencia en una expresión.
Integer contador;
void inc() {
contador++; //compile error
}
Pero hablaremos mas de esto mas adelante.
Si estas acostumbrado a usar JavaBeans, puedes entender a las referencias como una combinación de varias cosas.
Esto es porque no todos los valores son referencias como las que hemos visto; otras son como un método get, o algunas veces como un par de métodos get y set.
Nosotros necesitamos mostrar un equivalente de coordenadas cartesianas de una polar. Desde que las coordenadas cartesianas pueden ser computadas desde coordenadas polares, no necesitamos definir referencias state-holding. En vez, podemos definir los atributos como getters.
"Una coordenada polar"
class Polar(angulo, radio) {
shared Float angulo;
shared Float radio;
shared Float x => radio * cos(angulo);
shared Float y => radio * sin(angulo);
// ...
}
Nótese que la sintaxis de la declaración de un getter se muestra como la declaración de método sin una lista de parametros.
Entonces, ¿en qué manera son atributos “abstrayendo el estado”? Buenos, los códigos que utilicen un elemento Polar, no necesitan si es un atributo o un getter. Ahora que conocemos acerca de los getters, podemos reescribir nuestro atributo decripcion como un getter, sin afectar a cualquier código que lo use.
"Una cordenada polar con una etiqueta opcional"
class Polar(angulo, radio, String? etiqueta) {
shared Float angulo;
shared Float radio;
shared String descripcion {
if (exists etiqueta) {
descripcion = etiqueta;
}
else {
descripcion = "(``radio``,``angulo``)";
}
}
// ...
}
Pienso que es tiempo para las malas noticias: Ceylon no tiene sobrecarga de métodos o constructores (La verdad es que la sobrecarga es la fuentes de varios problemas en Java, especialmente cuando generics están en juego). Sin embargo podemos emular muchos usos no perjudiciales de la sobrecarga de constructores y métodos usando:
En este momentos no profundizaremos en detalles en cada una de las tres, pero veremos un pequeños ejemplo de cada una de las tres técnicas.
// parámetros por defaul
void println(String linea, String eol = "\n") =>
process.write(line + eol);
// Variadic parametros
void printlns(String* lineas) {
for (linea in lineas) {
println(linea);
}
}
// Tipo Union
void imprimeNombre(String|Named nombre) {
switch (nombre)
case (is String) {
println(nombre);
}
case (is Named){
print(nombre.pila + " " + nombre.apellido);
}
}
No te preocupes si aun no entiendes completamente el tercer ejemplo, volveremos a el mas tarde en el tour.
Hagamos uso de esta idea “sobrecargando” el “constructor” de Polar.
"Una cordenada polar con una etiqueta opcional"
class Polar(angulo, radio, String? etiqueta=null) {
shared Float angulo;
shared Float radio;
shared String descripcion {
if (exists etiqueta) {
descripcion = etiqueta;
}
else {
descripcion = "(``radio``,``angulo``)";
}
}
// ...
}
Ahora podemos crear una coordenada Polar, con o sin etiqueta:
Polar orig = Polar(0.0, 0.0, "origen");
Polar coord = Polar(r,theta);
En el siguiente capitulo, Continuaremos investigando acerca de los atributos y especialmente atributos variable. Pero también conoceremos conoceremos a las estructuras de control de Ceylon.