========================= Clases anónimas y miembro ========================= Esta es la cuarta parada en el tour de Ceylon. En la parada anterior hemos aprendido acerca de herencia y refinamientos. Es momento de completar nuestra introducción a la programación orientada a objetos en Ceylon aprendiendo acerca de las clases anónimas y clases miembro. --------------- Clases anónimas --------------- Si una clase no tiene parámetros, es posible usar una declaración corta que defina una instancia con nombre de la clase, sin proveer un nombre para la clase misma. Esto es usualmente de mas ayuda cuando estamos extendiendo una clases abstracta o implementando una interfaz. .. code-block:: ceylon doc "El origen" object origen extends Polar(0.0, 0.0) { descripcion => "origen"; } Una clase anónima puede extender una clase ordinaria y satisfacer una interfaz. .. code-block:: ceylon shared object consoleWriter satisfies Writer { formatter = StringFormatter(); write(String string) => process.write(string); } La desventaja de la declaración de un objeto es que no podemos escribir código que refiera a el tipo concreto de `origen` o `consoleWriter`, únicamente a la instancia con nombre. Tal vez estés tentado a pensar a la declaración de objetos como definición de `singletons`, pero eso no es del todo correcto. - La declaración de un objeto en `toplevel`, de hecho define un `singleton`. - La declaración de un objeto anidado en una clase define un objeto por cada instancia de la clase contenedora. - La declaración anidada en un método, getter, o setter da como resultado un nuevo objeto cada vez que el método, getter o setter es ejecutado. Veamos como esto puede ser útil: .. code-block:: ceylon interface Subscription { shared formal void cancel(); } Subscription register(Suscriber s) { subscribers.append(s); object subscription satisfies Subscription { shared actual void cancel() => subscribers.remove(s); } return subscription; } Note como este código de ejemplo hace hábilmente uso de el hecho que la declaración anidada recibe una closure de los valores contenidos en la declaración del método contenedor. Una manera distinta de entender acerca de la diferencia de un objeto y una clase es pensar que una clase es como un `object` con parámetros. (Por supuesto hay una gran diferencia: una declaración de una clase define un tipo con nombre y con esto podemos referirnos a el en otras partes del programa.) Como veremos después, Ceylon nos permite pensar en los métodos como atributos con parámetros. Un declaración `object` puede refinar un atributo declarado con `formal` o `default` mientras este sea un suptipo del tipo declarado en el atributo a refinar. .. code-block:: ceylon shared abstract class App() { shared formal OutputStream stream; ... } class ConsoleApp() extends App() { shared actual object stream satisfies OutputStream { ... } ... } Sin embargo, un `object` no podra ser mismo declarado `formal` o `default`. -------------------------------- Clases miembro y su refinamiento -------------------------------- Probablemente has usado anidar una clase dentro de un método o clase. Desde que Ceylon es un lenguaje con bloques de estructura recursivos, la idea de una clase anidada es mas que natural. Pero en Ceylon, una clase anidada no abstracta es un miembro del tipo contenedor. Por ejemplo, `BufferReader` define la clase miembro `Buffer`: .. code-block:: ceylon class BufferReader(Reader reader) satifies Reader { shared default class Buffer() satisfies List { ... } ... } La clase miembro `buffer` es anotada con `shared`, entonces podemos instanciar la clase de la siguiente manera: .. code-block:: ceylon BufferReader br = BufferReader(ExampleReader()); BufferReader.buffer b = br.Buffer(); Note que el tipo anidada deberá ser identificado junto con el tipo contenedor cuando es usada fuera de la clase. El miembro de la clase `Buffer` es también anotado con`default`, así que podemos refinarlo en un subtipo de `BufferReader`: .. code-block:: ceylon class BufferFileReader(File file) extends BufferReader(FileReader(file)) { shared actual class Buffer() extends super.Buffer() { ... } ... } Es correcto: ¡Ceylon nos permite "sobreescribir" una clase miembro de un supertipo! Note que `BufferFileReader.Buffer` es una subclase de `BufferReader.Buffer`. Ahora, la instancia anterior `br.buffer()`, !es una operación polimorfa¡ Puede devolver una instancia de `BufferReader.Buffer` o de `BufferReader.buffer`, dependiendo al que refiera `br` de `BufferReader` o `BufferFileReader`. Esto es mas que un lindo truco. La instanciación nos permite eliminar al concepto llamado "factory method pattern" de nuestro código. Es posible incluso, definir una clase miembro `formal`. Una clase miembro `formal` puede declarar miembros `formal`. .. code-block:: ceylon abstract class BufferReader (Reader reader) satisfies Reader { shared formal lass Buffer() { shared formal Byte read(); } ... } En este caso, una subclase concreta de la clase `abstract` deberá refinar los miembros `formal` de la clase. .. code-block:: ceylon shared class BufferFileReader(File file) extends BufferReader(FileReader(file)) { shared actual class Buffer() extends super.Buffer() { shared actual Byte read() { ... } } ... } Nótese la diferencia entre una clase `abstract` y una clase miembro `formal`. Una clase anidada `abstract` y no necesita ser refinada por subclases concretas de la clase contenedora. Una clase miembro `formal` puede ser instanciada y deberá ser refinada por cada subclase de la clase contenedora. Es un interesante ejercicio comparar el refinamiento de las clase miembro en Ceylon con la funcionalidad de inyección de dependencias en los frameworks con Java. Ambos mecanismos proveen un significado de abstraer la operación de instanciación de un tipo. Puedes pensar que las subclases que refinan un miembro tipo como llenando el mismo rol como una configuración de dependencia en un framework de inyección de dependencias. """"""""""" Aun hay mas """"""""""" Clases miembro y su refinamiento permiten a Ceylon soportar `type families`. No hablaremos de ello en este tour. En la siguiente estación, conoceremos a las secuencias, basandose Ceylon en el tipo "Array".