Clases

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.

Nombrando a los identificadores

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

Creando tus propias clases

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.

Con clase
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:

  1. Los parámetros usados para instanciar la clase son especificados como parte de la declaración de la clase. No existe un constructor al estilo de Java en Ceylon. Esta sintaxis es menos verbosa y mas regular que la de Java, C# o C++.
  2. Podemos hacer uso de los parámetros de una clase en cualquier lugar del cuerpo de la clase. En Ceylon, comúnmente no necesitamos definir explícitamente miembros de la clase para mantener valores. De hecho, podemos accesar a los parámetros angulo y radio directamente dentro de los métodos rotar y dilatar, y desde la expresión que especifica el valor de descripcion.

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.

Escondiendo los detalles de la implementación

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:

matryoshka doll
  • Elementos del programa que son visibles unicamente en el scope o alcance donde ellos fueron definidos, y
  • Elementos del programaque son visibles donde sea que el objeto donde el pertenece sea visible.

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.

Exponiendo parámetros como atributos

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.

Inicializando los atributos

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.

Abstrayendo estados usando atributos

Si estas acostumbrado a usar JavaBeans, puedes entender a las referencias como una combinación de varias cosas.

  • Un field
  • Un getter, y , algunas veces,
  • Un setter

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``)";
        }
    }

    // ...
}

Viviendo sin overloading

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:

  • Parámetros por defecto
  • Variadic parámetros (varargs), y
  • Tipos union y enumerated constraints

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);

Aun hay mas

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.