Está en la página 1de 2624

PIENSA EN JAVA

Fiaros de catalogacin bibliogrfica PIENSA EN JAVA

Cuarta Edicin

BRUCE ECKEL President, MindView, Inc.

Traduccin Vuelapluma

PEARSON

Fiaros de catalogacin bibliogrfica Prentice

Hall

Madrid Mexico Santa Fe de Bogota Buenos Aires Caracas Lima Montevideo San Juan San Jos Santiago So Paulo White Plains

Todos los derechos reservados.

Queda prohibida, salvo excepcin prevista en la Ley, cualquier forma de reproduccin, distribucin, comunicacin pblica y transformacin de esta obra sin contar con autorizacin de los titulares de propiedad intelectual. La infraccin de los derechos mencionados puede ser constitutiva de delito contra la propiedad intelectual (arts. 270 y sgls. Cdigo Penal).

Fiaros de catalogacin bibliogrfica DERECHOS RESERVADOS 2007 por PEARSON EDUCACIN S.A.

Ri be ra de l L oi ra , 2 8 2 8 0 4 2 M ad ri d PIENSA EN JAVA

Bruce Eckel ISBN: 978-84-8966-034-2

Fiaros de catalogacin bibliogrfica Deposito Legal; M-4.753-2007

PRENTICE HALL es un sello editorial autorizado de PEARSON EDUCACIN S.A.

Authorized translation from the English language edition, entitled THINKING IN JAVA, 4,h Edition by ECKJ-L BRUCE, published by Pearson Education Inc, publishing as Prentice Hall, Copyright 2006 EQUIPO EDITORIAL

Editor: Miguel MartinRomo Tcnico editorial: Marta Caicoya EQUIPO DE PRODUCCIN:

Fiaros de catalogacin bibliogrfica Direct or: Jos A. Clare s Tcni co: Maria Alvea r Diseo de Cubierta: Equipo de diseo de Pearson Educacin S.A. Impreso por: Grficas Rogar, S. A.

IMPRESO EN ESPAA - PRINTED IN SPAIN Este libro ha sido impreso con papel y tintas ecolgicosDedicatoria A Daw nPrefacio

Originalmente, me enfrent a Java como si friera simplemente otro lenguaje ms de programacin, So cual es cierto en muchos sentidos.

Pero, a medida que fue pasando el tiempo y lo fui estudiando con mayor detalle, comenc a ver que el objetivo fundamental de este lenguaje era distinto de los dems lenguajes que haba visto hasta el momento.

La tarea de programacin se basa en gestionar la complejidad: la complejidad del problema que se quiere resolver, sumada a la complejidad de la mquina en la cual hay que resolverlo. Debido a esta complejidad, la mayora de los proyectos de programacin terminan fallando. A pesar de lo cual, de todos los lenguajes de programacin que conozco, casi ninguno de ellos haba adoptado como principal objetivo de diseo

Fiaros de catalogacin bibliogrfica resolver la complejidad inherente al desarrollo y el mantenimiento de los programas. 1 Por supuesto, muchas decisiones del diseo de lenguajes se realizan teniendo presente esa complejidad, pero siempre termina por considerarse esencial introducir otros problemas dentro del conjunto de los objetivos. Inevitablemente, son estos otros problemas los que hacen que los programadores terminen no pudiendo cumplir el objetivo principalmente con esos lenguajes. Por ejemplo, C++ tena que ser compatible en sentido descendente con C (para permitir una fcil migracin a los programadores de C), adems de ser un lenguaje eficiente. Ambos objetivos resultan muy tiles y explican paite del xito de C++, pero tambin aaden un grado adicional de complejidad que impide a algunos proyectos finalizar (por supuesto, podemos echar la culpa a los programadores y a los gestores, pero si un lenguaje puede servir de ayuda detectando los errores que cometemos, por qu no utilizar esa posibilidad?). Otro ejemplo, Visual BASIC (VB) estaba ligado a BASIC, que no haba sido diseado para ser un lenguaje extensiblc, por lo que todas las extensiones aadidas a VB han dado como resultado una sintaxis verdaderamente inmanejable. Perl es compatible en sentido descendente con awk, sed, grep y otras herramientas Unix a las que pretenda sustituir, y como resultado, se le acusa a menudo de generar "cdigo de slo escritura" (es decir, despus de pasado un tiempo se vuelve completamente ilegible). Por otro lado, C++, VB, Perl y otros lenguajes como Smalltalk han centrado algo de esfuerzo de diseo en la cuestin de la complejidad, y como resultado, ha tenido un gran xito a la hora de resolver ciertos tipos de problemas.

Lo que ms me ha impresionado cuando he llegado a entender el lenguaje Java es que dentro del conjunto de objetivos de diseo establecido por Sun, parece que se hubiera decidido tratar de reducir la complejidad para el programador Como si quienes marcaron esos objetivos hubieran dicho: "Tratemos de reducir el tiempo y la dificultad necesarios para generar cdigo robusto". Al principio, este objetivo daba como resultado un cdigo que no se ejecutaba especialmente rpido (aunque esto ha mejorado a lo largo del tiempo), pero desde luego ha permitido reducir considerablemente el tiempo de desarrollo, que es inferior en un 50 por ciento o incluso ms al tiempo necesario para crear un programa en C-H- equivalente. Simplemente por esto, ya podemos ahorrar cantidades enormes de tiempo y de dinero, pero Java no se detiene ah, sino que trata de hacer transparentes muchas de las complejas tareas que han llegado a ser importantes en el mundo de la programacin, como la utilizacin de mltiples hebras o la programacin de red, empleando para conseguir esa transparencia una serie de caractersticas del lenguaje y de bibliotecas preprogramadas que pueden hacer que muchas tareas lleguen a resultar sencillas. Finalmente, Java aborda algunos problemas realmente complejos: programas interplalaforma, cambios de cdigo dinmicos e incluso cuestiones de seguridad, todos los cuales representan problemas de una complejidad tal que pueden hacer fracasar proyectos completos de programacin. Por tanto, a pesar de los problemas de prestaciones, las oportunidades que Java nos proporciona son inmensas, ya que puede 1 Sin embargo, creo que el lenguaje Python es el que ms se acerca a ese objetivo. Consulte wmv.Python.org.

Fiaros de catalogacin bibliogrfica incrementar significativamente nuestra productividad como programadores.

Java incrementa el ancho de banda de comunicacin entre las personas en todos los sentidos: a la hora de crear los programas, a la hora de trabajar en grupo, a la hora de construir interfaces para comunicarse con los usuarios, a la hora de

ejecutar los programas en diferentes tipos de mquinas y a la hora de escribir con sencillez aplicaciones que se comuniquen a travs de Internet.

En mi opinin, los resultados de la revolucin de las comunicaciones no se percibirn a partir de los efectos de desplazar grandes cantidades de bits de un sitio a otro, sino que seremos conscientes de la verdadera revolucin a medida que veamos cmo podemos comunicarnos con los dems de manera ms sencilla, tanto en comunicaciones de persona a persona, como en grupos repartidos por todo el mundo. Algunos sugieren que la siguiente revolucin ser la formacin de una especie de mente global derivada de la interconexin de un nmero suficiente de personas. No s si Java llegar a ser la herramienta que fomente dicha revolucin, pero esa posibilidad me ha hecho sentir, al menos, que estoy haciendo algo importante al tratar de ensear este lenguaje.

Java SE5 y SE6

Esta edicin del libro aprovecha en buena medida las mejoras realizadas al lenguaje Java en lo que Sun originalmente denomin JDK 1.5 y cambi posteriormente a JDK.5 o J2SE5. Posteriormente, la empresa elimin el obsoleto 2 y cambi el nombre a Java SE5. Muchos de los cambios en el lenguaje Java SE5 fueron decididos para mejorar la experiencia de uso del programador. Como veremos, los diseadores del lenguaje Java no obtuvieron un completo xito en esta tarea, pero en general dieron pasos muy significativos en la direccin correcta.

Uno de los objetivos ms importantes de esta edicin es absorber de manera completa las mejoras introducidas por Java SE5/6, presentarlas y emplearlas a lo largo de todo el texto. Esto quiere decir que en esta edicin se ha tomado la dura decisin de hacer el texto nicamente compatible con Java SE5/6, por lo que buena parte del cdigo del libro

Fiaros de catalogacin bibliogrfica no puede compilarse con las versiones anteriores de Java; el sistema de generacin de cdigo dar errores y se detendr si se intenta efectuar esa compilacin. A pesar de todo, creo que los beneficios de este enfoque compensan el riesgo asociado a dicha decisin.

Si el lector prefiere por algn motivo las versiones anteriores de Java, se puede descargar el texto de las versiones anteriores de este libro (en ingls) en la direccin mvw.MindView.net. Por diversas razones, la edicin actual del libro no est en formato electrnico gratuito, sino que slo pueden descargarse las ediciones anteriores. Java SE6

La redaccin de este libro ha sido, en si misma, un proyecto de proporciones colosales y al que ha habido que dedicar muchsimo tiempo. Y antes de que el libro fuera publicado, la versin Java SE6 (cuyo nombre en clave es mustang) apareci en versin beta. Aunque hay unos cuantos cambios de menor importancia en Java SE6 que mejoran algunos de los ejemplos incluidos en el libro, el tratamiento de Java SE6 no ha afectado en gran medida al contenido del texto; las principales mejoras de la nueva versin se centran en el aumento de la velocidad y en determinadas funcionalidades de biblioteca que caan fuera del alcance del texto.

El cdigo incluido en el libro ha sido comprobado con una de las primeras versiones comerciales de Java SE6, por lo que no creo que vayan a producirse cambios que afecten al contenido del texto. Si hubiera algn cambio importante a la hora de lanzar oficialmente JavaSE, ese cambio se ver reflejado en el cdigo fuente del libro, que puede descargarse desde www Mind View. net.

En la portada del libro se indica que este texto es para Java SE5/6, lo que significa escrito para Java SE5 teniendo en cuenta los significativos cambios que dicha versin ha introducido en el lenguaje, pero siendo el texto igualmente aplicable a Java SE6.

La cuarta edicin

La principal satisfaccin a la hora de realizar una nueva edicin de un libro es la de

Fiaros de catalogacin bibliogrfica poder corregir el texto, aplicando todo aquello que he podido aprender desde que la ltima edicin viera la luz. A menudo, estas lecciones son derivadas de esa frase que dice: Aprender es aquello que conseguimos cuando no conseguimos lo que queremos", y escribir una nueva edicin del libro constituye siempre una oportunidad de corregir errores o hacer ms amena la lectura. Asimismo, a la hora de abordar una nueva edicin vienen a la mente nuevas ideas fascinantes y la posibilidad de cometer nuevos errores se ve ms que compensada por el placer de descubrir nuevas cosas y la capacidad de expresar las deas de una fonna ms adecuada.

Asimismo, siempre se tiene presente, en el fondo de la mente, ese desafo de escribir un libro que los poseedores de las ediciones anteriores estn dispuestos a comprar. Ese desafo me anima siempre a mejorar, reescribir y reorganizar todo lo que puedo, con el fin de que el libro constituya una experiencia nueva y valiosa para los lectores ms fieles.

Cambios

El CD-ROM que se haba incluido tradicionalmentc como parte del libro no ha sido incluido en esta edicin. La parte esencial de dicho CD, el seminario multimedia Thinking in C (creado para MindVicw por Chuck Allison), est ahora disponible como presentacin Flash descargable. El objetivo de dicho seminario consiste en preparar a aquellos que no estn lo suficientemente familiarizados con la sintaxis de C, de manera que puedan comprender mejor el material presentado en este libro. Aunque en dos de los captulos del libro se cubre en buena medida la sintaxis a un nivel introductorio, puede que no sean suficientes para aquellas personas que carezcan de los conocimientos previos adecuados, y la presentacin Thinking in C trata de ayudar a dichas personas a alcanzar el nivel necesario.

Fiaros de catalogacin bibliogrfica El captulo dedicado a la concurrencia, que antes llevaba por ttulo Programacin multihebra, ha sido completamente reescrito con el fin de adaptarlo a los cambios principales en las bibliotecas de concurrencia de Java SE5, pero sigue proporcionando informacin bsica sobre las ideas fundamentales en las que la concurrencia se apoya. Sin esas ideas fundamentales, resulta difcil comprender otras cuestiones ms complejas relacionadas con la programacin multihebra. He invertido muchos meses en esta tarea, inmerso en ese mundo denominado concurrencia y el resultado final es que el captulo no slo proporciona los fundamentos del tema sino que tambin se aventura en otros territorios ms novedosos.

Existe un nuevo captulo dedicado a cada una de las principales caractersticas nuevas del lenguaje Java SE5, y el resto de las nuevas caractersticas han sido reflejadas en las modificaciones realizadas sobre el material existente. Debido al estudio continuado que realizo de los patrones de diseo, tambin se han introducido en todo el libro nuevos patrones.

El libro ha sufrido una considerable reorganizacin. Buena parte de los cambios se deben a razones pedaggicas, junto con la perfeccin de que quiz mi concepto de captulo necesitaba ser revisado. Adicionalmentc, siempre he tendido a creer que un tema tena que tener la suficiente envergadura para justificar el dedicarle un captulo. Pero luego he visto, especialmente a la hora de ensear los patrones de diseo, que las personas que asistan a los seminarios obtenan mejores resultados si se presentaba un nico patrn y a continuacin se haca, inmediatamente, un ejercicio, incluso si eso significaba que yo slo hablara durante un breve perodo de tiempo (asimismo, descubr que esta nueva estructura era ms agradable para el profesor). Por tanto, en esta versin del libro he tratado de descomponer los captulos segn los temas, sin preocuparme de la longitud final de cada capmlo. Creo que el resultado representa una autntica mejora.

Tambin he llegado a comprender la enorme importancia que tiene el tema de las pruebas de cdigo. Sin un marco de pruebas predefinido, con una serie de pruebas que se ejecuten cada vez que se construya el sistema, no hay forma de saber si el cdigo es fiable o no. Para conseguir este objetivo en el libro, he creado un marco de pruebas que permite mostrar y validar la salida de cada programa (dicho marco est escrito en Python. y puede descargarse en www.MindView.net. El tema de las pruebas, en general, se trata en el suplemento disponible en http://www.MindView.net/Books/BetterJava, que presenta lo que creo que son capacidades fundamentales que todos los programadores deberan tener como parte de sus conocimientos bsicos.

Fiaros de catalogacin bibliogrfica Adems, he repasado cada uno de los ejemplos del libro preguntndome a m mismo: Por qu lo hice de esta manera?. En la mayora de los casos, he realizado algunas modificaciones y mejoras, tanto para hacer los ejemplos ms coherentes entre s, como para demostrar lo que considero que son las reglas prcticas de programacin en Java, (al menos dentro de los limites de un texto introductorio). Para muchos de los ejemplos existentes, se ha realizado un rediseo y una nueva implementacin con cambios significativos con respecto a las versiones anteriores. Aquellos ejemplos que me pareca que ya no tenan sentido han sido eliminados y se han aadido, asimismo, nuevos ejemplos.

Los lectores de las ediciones anteriores han hecho numerossimos comentarios muy pertinentes, lo que me llena de satisfaccin. Sin embargo, de vez en cuando tambin me llegan algunas quejas y, por alguna razn, una de las ms frecuentes es que este libro es demasiado voluminoso. En mi opinin, si la nica queja es que este libro tiene demasiadas pginas, creo que el resultado global es satisfactorio (se me viene a la mente el comentario de aquel emperador de Austria que se quejaba de la obra de Mozart diciendo que tena demasiadas notas; por supuesto, no trato en absoluto de compararme con Mozart). Adems, debo suponer que ese tipo de quejas proceden de personas que todava no han llegado a familiarizarse con la enorme variedad de caractersticas del propio lenguaje Java y que no han tenido ocasin de consultar el resto de libros dedicados a este tema. De todos modos, una de las cosas que he tratado de hacer en esta edicin es recortar aquellas partes

que han llegado a ser obsoletas, o al menos, no esenciales. En general, se ha repasado todo el texto eliminando lo que ya haba dejado de ser necesario, incluyendo los cambios pertinentes y mejorando el contenido de la mejor manera posible. No me importa demasiado eliminar algunas partes, porque el material original correspondiente contina estando en el sitio web (www.MindView.net), gracias a la versin descargable de las tres primeras ediciones del libro. Asimismo, el lector tiene a su disposicin material adicional en suplementos descargablcs de esta edicin.

En cualquier caso, para aquellos lectores que sigan considerando excesivo el tamao del libro les pido disculpas. Lo crean

o no. he hecho cuanto estaba en mi mano para que ese tamao fuera el menor posible.

Fiaros de catalogacin bibliogrfica Sobre el diseo de la cubierta

La cubierta del libro est inspirada por el movimiento American Arts & Crafts Movement que comenz poco antes del cambio de siglo y alcanz su cnit entre 1900 y 1920. Comenz en Inglaterra como reaccin a la produccin en masa de la revolucin industrial y al estilo altamente ornamental de la poca victoriana. Arts & Crafts enfatizaba el diseo con formas naturales, como en el movimiento art nouvean, como el trabajo manual y la importancia del artesano, sin por ello renunciar al uso de herramientas modernas. Existen muchos ecos con la situacin que vivimos hoy en da: el cambio de siglo, la evolucin desde los rudimentarios comienzos de la revolucin informtica hacia algo ms refinado y significativo y el nfasis en la artesana del software en lugar de en su simple manufactura.

La concepcin de Java tiene mucho que ver con este punto de vista. Es un intento de elevar al programador por encima del sistema operativo, para transformarlo en un artesano del software.

Tanto el autor de este libro como el diseador de la cubierta (que son amigos desde la infancia) han encontrado inspiracin en este movimiento, ambos poseemos muebles, lmparas y otros objetos originales de este periodo o inspirados en el mismo.

H1 otro tema de la cubierta sugiere una vitrina coleccionista que un naturalista podra emplear para mostrar los especmenes de insectos que ha preservado. Estos insectos son objetos situados dentro de los objetos compartimento. Los objetos compartimento estn a su vez, colocados dentro del objeto cubierta, lo que ilustra el concepto de agregacin dentro de la programacin orientada a objetos. Por supuesto, cualquier programador de habla inglesa efectuar enseguida entre los insectos bugs y los errores de programacin (tambin bugs). Aqu, esos insectos/errores han sido capturados y presumiblemente muertos en un tarro y confinados finalmente dentro de una vitrina, con lo que tratamos de sugerir la habilidad que Java tiene para encontrar, mostrar y corregir los errores (habilidad que constituye uno de sus ms potentes atributos).

En esta edicin, yo me he encargado de la acuarela que puede verse como fondo de la cubierta.

Fiaros de catalogacin bibliogrfica Agradecimientos

En primer lugar, gracias a todos los colegas que han trabajo conmigo a la hora de impartir seminarios, realizar labores de consultora y desarrollar proyectos pedaggicos: Dave Bartlett, Bill Venners, Chuck Allison, Jeremy Meyer y Jamie King. Agradezco la paciencia que mostris mientras contino tratando de desarrollar el mejor modelo para que una serie de personas independientes como nosotros puedan continuar trabajando juntos. Recientemente, y gracias sin duda a Internet, he tenido la oportunidad de relacionarme con un nmero sorprendentemente grande de personas que me ayudan en mi trabajo, usualmente trabajando desde sus propias oficinas. En el pasado, yo tendra que haber adquirido o alquilado una gran oficina para que todas estas personas pudieran trabajar, pero gracias a Internet, a los servicios de mensajeros y al telfono, ahora puedo contar con su ayuda sin esos costes adicionales. Dentro de mis intentos por aprender a trabajar eficazmente con los dems, todos vosotros me habis ayudado enormemente y espero poder continuar aprendiendo a mejorar mi trabajo gracias a los esfuerzos de otros. La ayuda de Paula Stcucr ha sido valiossima a la hora de tomar mis poco inteligentes prcticas empresariales y transformarlas en algo razonable (gracias por ayudarme cuando no quiero encargarme de algo concreto, Paula). Jonathan Wilcox. Esq., se encarg de revisar la estructura de mi empresa y de eliminar cualquier piedra que pudiera tener un posible escorpin, hacindonos marchar disciplinadamente a travs del proceso de poner todo en orden desde el punto de vista legal, gracias por tu minuciosidad y tu persistencia. Sharlyim Cobaugh ha llegado a convertirse en una autntica experta en edicin de sonido y ha sido una de las personas esenciales a la hora de crear los cursos de formacin multimedia, adems de ayudar en la resolucin de muchos otros problemas. Gracias por la perseverancia que has demostrado a la hora de enfrentarte con problemas informticos complejos. La gente de Amaio en Praga tambin ha sido de gran ayuda en numerosos proyectos. Daniel Will-Harris fue mi primera ftien -te de inspiracin en lo que respecta al proyecto de trabajo a travs de Internet y tambin ha sido imprescindible, por supuesto, en todas las soluciones de diseo grfico que he desarrollado.

A lo largo de los aos, a travs de sus conferencias y seminarios, Gerald Weinberg se ha convertido en mi entrenador y mentor extraoficial, por lo que le estoy enormemente agradecido.

Ervin Varga ha proporcionado numerosas correcciones tcnicas para la cuarta edicin, aunque tambin hay otras personas que han ayudado en esta tarea, con diversos captulos

Fiaros de catalogacin bibliogrfica y ejemplos. Ervin ha sido el revisor tcnico principal del libro y tambin se encarg de escribir la gua de soluciones para la cuarta edicin. Los errores detectados por Ervin y las mejoras que l ha introducido en el libro han permitido redondear el texto. Su minuciosidad y su atencin al detalle resultan sorprendentes y es, con mucho, el mejor revisor tcnico que he tenido. Muchas gracias, Ervin.

Mi weblog en la pgina www.Artima.com de Bill Venners tambin ha resultado de gran ayuda a la hora de verificar determinadas ideas. Gracias a los lectores que me han ayudado a aclarar los conceptos enviando sus comentarios; entre esos lectores debo citar a James Watson, Howard Lovatt. Michael Barker, y a muchos otros que no menciono por falta de espacio, en particular a aquellos que me han ayudado en el tema de los genricos.

Gracias a Mark Welsh por su ayuda continuada.

Evan Cofsky contina siendo de una gran ayuda, al conocer de memoria todos los arcanos detalles relativos a la configuracin y mantenimiento del servidor web basados en Linux, as como a la hora de mantener optimizado y protegido el servidor MindView.

Gracias especiales a mi nuevo amigo el caf, que ha permitido aumentar enormemente el entusiasmo por el proyecto. Camp4 Coffee en Crested Butte, Colorado, ha llegado a ser el lugar de reunin normal cada vez que alguien vena a los seminarios de MindView y proporciona el mejor catering que he visto para los descansos en el seminario. Gracias a mi colega Al Smith por crear ese caf y por convertirlo en un lugar tan extraordinario, que ayuda a hacer de Crested Butte un lugar mucho ms interesante y placentero. Gracias tambin a lodos los camareros de Camp4, que tan servicialmente atienden a sus clientes.

Gracias a la gente de Prentice Hall por seguir atendiendo a todas mis peticiones, y por facilitarme las cosas en todo momento.

Hay varias herramientas que han resultado de extraordinaria utilidad durante el proceso de desarrollo y me siento en deuda con sus creadores cada vez que las uso. Cygwin (wmv.cygwin.com) me ha permitido resolver innumerables problemas que Windows no puede resolver y cada da que pasa ms me engancho a esta herramienta (me hubiera

Fiaros de catalogacin bibliogrfica encantado disponer de ella hace 15 aos, cuando tena mi mente orientada a Gnu Emacs). Eclipse de IBM (www.eciipsc.org) representa una maravillosa contribucin a la comunidad de desarrolladores y cabe esperar que se puedan obtener grandes cosas con esta herramienta a medida que vaya evolucionando. JetBrains IntelliJ Idea contina abriendo nuevos y creativos caminos dentro del campo de las herramientas de desarrollo.

Comenc a utilizar Enterprise Architect de Sparxsvstems con este libro y se ha convertido rpidamente en mi herramienta UML favorita. El formateador de cdigo Jalopy de Marco Hunsicker (www.triemax.com) tambin ha resultado muy til en numerosas ocasiones y Marco me ha ayudado extraordinariamente a la hora de configurarlo para mis necesidades concretas. En mi opinin, la herramienta JEdit de Slava Pcstov y sus correspondientes plug-ins tambin resultan tiles en diversos momentos (www.jedit.org); esta herramienta es un editor muy adecuado para todos aquellos que se estn iniciando en el desarrollo de seminarios.

por supuesto, por si acaso no lo he dejado claro an, utilizo constantemente Python (www.Python.org) para resolver problemas, esta herramienta es la criatura de mi colega Guido Van Rossum y de la panda de enloquecidos genios con los que disfrut enormemente haciendo deporte durante unos cuantos das (a Tim Peters me gustara decirle que he enmarcado ese ratn que tom prestado, al que le he dado el nombre oficial de TimBotMouse). Permitidme tan slo recomendaros que busquis otros lugares ms sanos para comcr. Asimismo, mi agradecimiento a toda la comunidad Python, formada por un conjunto de gente extraordinaria. Son muchas las personas que me han hecho llegar sus correcciones y estoy en deuda con todas ellas, pero quiero dar las gracias en particular a (por la primera edicin): Kevin Raulerson (encontr numerossimos errores imperdonables), Bob Rcscndcs (simplemente increble), John Pinto, Joe Dante, Joe Sharp (fabulosos vuestros comentarios), David Combs (numerosas correcciones de clarificacin y de gramtica). Dr. Robert Stephenson, John Cook, Franklin Chen, Zev Griner, David K.arr, Leander A. Stroschein. Steve Clark, Charles A. Lee. Austin Maher, Dennis P. Roth, Roque Oliveira. Douglas Dunn, Dejan Ristic, Neil Galameau, David B. Malkovsky, Steve Wilkinson, y muchos otros. El Profesor Marc Meurrens dedic
Y

una gran cantidad de esfuerzo a publieitar y difundir la versin electrnica de la primera edicin de esle libro en Europa.

Gracias a todos aquellos que me han ayudado a reescribir los ejemplos para utilizar la biblioteca Swing (para la segunda edicin), as como a los que han proporcionado otros tipos de comentarios: Jon Shvarts, Thomas Kirsch, Rahim Adatia, Rajesh Jain. Ravi Manthena. tanu Rajamani, Jens Brandt. Nitin Shivaram. Malcolm Davis y a todos los dems que me han manifestado su apoyo.

En la cuarta edicin, Chris Grindstaff result de gran ayuda durante el desarrollo de la seccin SWT y Sean Neville escribi para m el primer borrador de la seccin dedicada a Flex.

Kraig Brockschmidt y Gen Kiyooka son algunas de esas personas inteligentes que he podido conocer en algn momento de vida y que han llegado a ser autnticos amigos, habiendo tenido una enorme influencia sobre m. Son personas poco usuales en el sentido de que practican yoga y otras formas de engrandecimiento espiritual, que me resultan particularmente inspiradoras e instructivas.

Me resulta sorprendente que el saber de Delphi me ayudara a comprender Java, ya que ambos lenguajes tienen en comn muchos conceptos y decisiones relativas al diseo del lenguaje. Mis amigos de Delphi me ayudaron enormemente a la hora de entender mejor ese maravilloso entorno de programacin. Se trata de Marco Cantu (otro italiano, quiz el ser educado en latn mejora las aptitudes de uno para los lenguajes de programacin), Neil Rubenking (que sola dedicarse al yoga, la comida vegetariana y el Zen hasta que descubri las computadoras) y por supuesto Zack Urlocker (el jefe de producto original de Delphi), un antiguo amigo con el que he recorrido el mundo. Todos nosotros estamos en deuda con el magnfico Anders Hejlsberg, que contina asombrndonos con C# (lenguaje que, como veremos en el libro, fue una de las principales inspiraciones para Java SE5).

Los consejos y el apoyo de mi amigo Richard Hale Shaw (al igual que los de Kim) me han sido de gran ayuda. Richard y yo hemos pasado muchos meses juntos impartiendo seminarios y tratando de mejorar los aspectos pedaggicos con el fin de que los asistentes disfrutaran de una experiencia perfecta.

El diseo del libro, el diseo de la cubierta y la fotografa de la cubierta han sido realizados por mi amigo Daniel Will-Harris, renombrado autor y diseador ('m\w. WiU-Harris.com), que ya sola crear sus propios diseos en el colegio, mientras esperaba a que se inventaran las computadoras y las herramientas de autoedicin, y que ya entonces se quejaba de mi forma de resolver los problemas de lgebra. Sin embargo, yo me he encargado de preparar para imprenta las pginas, por lo que los errores de fotocomposicin son mos. He utilizado Microsoft Word XP para Windows a la hora de escribir el libro y de preparar las pginas para imprenta mediante Adobe Acrobat: este libro fue impreso directamente a partir de los archivos Acrobat PDF. Como tributo a la era electrnica yo me encontraba fuera del pas en el momento de producir la primera y la segunda ediciones finales del libro; la primera edicin fue enviada desde Ciudad del Cabo (Sudfrica), mientras que la segunda edicin fue enviada desde Praga. La tercera y cuarta ediciones fueron realizadas desde Crested Butte, Colorado. En

la versin en ingls del libro se utiliz el tipo de letra Georgia para el texto y los ttulos estn enVerdana. Laletrade la

18 Piensa en Java

cubierta original es ITC Rennie Mackintosh.

Gracias en especial a todos mis profesores y estudiantes (que tambin son mis profesores).

Mi gato Molly sola sentarse en mi regazo mientras trabajaba en esta edicin, ofrecindome as mi propio tipo de apoyo peludo y clido. Entre los amigos que tambin me han dado su apoyo, y a los que debo citar (aunque hay muchos otros a los que no cito por falta de espacio), me gustara destacar a: Patty Gast (extraordinaria masajista), Andrew Binstock, Stcve Sinofsky, JD Hildebrandt, Tom Keffer, Brian McElhinney, Brinkley Barr, Bill Gates en Midmght Engineering Magazine, Larry Constantine y Lucy Lockwood, Gene Wang, Dave Mayer, David Intcrsimonc, Chris y Laura Strand, los Almquists, Brad Jerbic. Marilyn Cvitanic. Mark Mabry. la familia Robbins. la familia Moelter (y los McMillans), Michacl Wilk, Dave Stoner, los Cranstons, Larry Fogg, Mike Sequeira, Gary Entsminger, Cevin y Sonda Donovan, Joe Lordi, Dave y Brenda Bartlett, Patti Gast, Blake, Annette & Jade, los Rentschlers, los Sudeks, Dick, Patty, y Lee Eckel, Lynn y Todd, y sus familias. Y por supuesto, a mam y pap.Resumen del contenido

Contenido

Contenido 19

El controlador de invernadero

implementado

Introduccin

El dio al hombre la capacidad de hablar, y de esa capacidad surgi el pensamiento. Que es la

medida del Universo Prometeo desencadenado* Shelley Los seres humanos ... estamos, en buena medida, a merced de! lenguaje concreto que nuestra sociedad haya elegido como medio de expresin. Resulta completamente ilusorio creer que nos ajustamos a la realidad esencialmente sin utilizar el lenguaje y que el lenguaje es meramente un medio incidental

20 Piensa en Java de resolver problemas especficos de comunicacin y reflexin. Lo cierto es que el mundo real est en gran parte construido, de manera inconsciente, sobre los hbitos lingsticos del grupo. El estado de la Lingistica como ciencia, 1929, Edward Sapir

Como cualquier lenguaje humano. Java proporciona una forma de expresar conceptos. Si tiene xito, esta forma de expresin ser significativamente ms fcil y flexible que las alternativas a medida que los problemas crecen en tamao y en complejidad.

No podemos ver Java slo como una coleccin de caractersticas, ya que algunas de ellas no tienen sentido aisladas. Slo se puede emplear la suma de las partes si se est pensando en el diseo y no simplemente en la codificacin. Y para entender Java as, hay que comprender los problemas del lenguaje y de la programacin en general. Este libro se ocupa de los problemas de la programacin, porque son problemas, y del mtodo que emplea Java para resolverlos. En consecuencia, el conjunto de caractersticas que el autor explica en cada captulo se basa en la forma en que l ve cmo puede resolverse un tipo de problema en particular con este lenguaje. De este modo, el autor pretende conducir, poco a poco, al lector hasta el punto en que Java se convierta en su lengua materna.

La actitud del autor a lo largo del libro es la de conseguir que el lector construya un modelo mental que le permita desarrollar un conocimiento profundo del lenguaje; si se enfrenta a un puzzle, podr fijarse en el modelo para tratar de deducir la respuesta. Prerrequisitos

Este libro supone que el lector est familiarizado con la programacin: sabe que un programa es una coleccin de instrucciones, qu es una subrutina, una funcin o una macro, conoce las instrucciones de control como if* y las estructuras de bucle como while, etc. Sin embargo, es posible que el lector haya aprendido estos conceptos en muchos sitios, tales como la programacin con un lenguaje de macros o trabajando con una herramienta como Perl. Cuando programe sintindose cmodo con las ideas bsicas de la programacin, podr abordar este libro. Por supuesto, el libro ser ms fcil para los programadores de C y ms todava para los de C++, pero tampoco debe autoexcluirse si no tiene experiencia con estos lenguajes (aunque tendr que trabajar duro). Puede descargarse en www.MindView.net el seminario multimedia Thinking in C, el cual le ayudar a aprender ms rpidamente los fundamentos necesarios para estudiar Java. No

Contenido 21 obstante, en el libro se abordan los conceptos de programacin orientada a objetos (POO) y los mecanismos de control bsicos de Java.

Aunque a menudo se hacen referencias a las caractersticas de los lenguajes C y C++ no es necesario profundizar en ellos, aunque s ayudarn a todos los programadores a poner a Java en perspectiva con respecto a dichos lenguajes, de los que al fin y al cabo desciende. Se ha intentado que estas referencias sean simples y sirvan para explicar cualquier cosa con la que una persona que nunca haya programado en C/C -+ no est familiarizado. Aprendiendo Java

Casi al mismo tiempo que se public mi primer libro, Using C++ (Osbome/McGrawHill, 1989). comenc a ensear dicho lenguaje. Ensear lenguajes de programacin se convirti en mi profesin; desde 1987 he visto en auditorios de todo el mundo ver dudar a los asistentes, he visto asimismo caras sorprendidas y expresiones de incredulidad. Cuando empec a impartir cursos de formacin a grupos pequeos, descubr algo mientras se hacan ejercicios. Incluso aquellos que sonrean se quedaban con dudas sobre muchos aspectos. Comprend al dirigir durante una serie de aos la sesin de C-H* en la Software Development Conference (y ms tarde la sesin sobre Java), que tanto yo como otros oradores tocbamos demasiados temas muy rpidamente. Por ello, tanto debido a la variedad en el nivel de la audiencia como a la forma de presentar el material, se termina perdiendo audiencia. Quiz es pedir demasiado pero dado que soy uno de esos que se resisten a las conferencias tradicionales (y en la mayora de los casos, creo que esa resistencia proviene del aburrimiento), quera intentar algo que permitiera tener a todo el mundo enganchado.

Durante algn tiempo, cre varias presentaciones diferentes en poco tiempo, por lo que termine aprendiendo segn el mtodo de la experimentacin e iteracin (una tcnica que tambin inciona en el diseo de programas). Desarroll un curso utilizando todo lo que habia aprendido de mi experiencia en la enseanza. Mi empresa, MindView, Inc., ahora imparte el seminario Thinking in Java (piensa en Java); que es nuestro principal seminario de introduccin que proporciona los fundamentos para nuestros restantes seminarios ms avanzados. Puede encontrar informacin detallada en www.MindView.net. El seminario de introduccin tambin est disponible en el CDROM Hamls-On Java. La informacin se encuentra disponible en el mismo sitio web.

La respuesta que voy obteniendo en cada seminario me ayuda a cambiar y reenfocar el material hasta que creo que funciona bien como mtodo de enseanza. Pero este libro no

22 Piensa en Java son slo las notas del seminario; he intentado recopilar el mximo de informacin posible en estas pginas y estructurarla de manera que cada tema lleve al siguiente. Ms que cualquier otra cosa, el libro est diseado para servir al lector solitario que se est enfrentando a un nuevo lenguaje de programacin. Objetivos

Como mi anterior libro, Thinking in C++, este libro se ha diseado con una idea en mente: la forma en que las personas aprenden un lenguaje. Cuando pienso en un capitulo del libro, pienso en trminos de qu hizo que fuera una leccin durante un seminario. La informacin que me proporcionan las personas que asisten a un seminario me ayuda a comprender cules son las partes complicadas que precisan una mayor explicacin. En las reas en las que fui ambicioso e inclu demasiadas caractersticas a un mismo tiempo, pude comprobar que si inclua muchas caractersticas nuevas, tena que explicarlas y eso contribua fcilmente a la confusin del estudiante.

En cada captulo he intentado ensear una sola caracterstica o un pequeo grupo de caractersticas asociadas, sin que sean necesarios conceptos que todava no se hayan presentado. De esta manera, el lector puede asimilar cada pieza en el contexto de sus actuales conocimientos.

Mis objetivos en este libro son los siguientes:

1.

Presentar el material paso a paso de modo que cada idea pueda entenderse fcilmente antes de pasar a la siguiente. Secuenciar cuidadosamente la presentacin de las caractersticas, de modo que se haya explicado antes de que se vea en un ejemplo. Por supuesto, esto no siempre es posible, por lo que en dichas situaciones, se proporciona una breve descripcin introductoria.

2.

Utilizar ejemplos que sean tan simples y cortos como sea posible. Esto evita en ocasiones acometer problemas del mundo real, pero he descubierto que los principiantes suelen estar ms contentos cuando pueden comprender todos los detalles de un ejemplo que cuando se ven impresionados por el mbito del problema que resuelve. Tambin, existe una seria limitacin en cuanto a la cantidad de cdigo que se puede absorber en el aula. Por esta razn, no dudar en recibir crticas acerca del uso de ejemplos de juguete, sino que estoy deseando recibirlas en aras de lograr algo pedaggicamente til.

Contenido 23 Dar lo que yo creo que es importante para que se comprenda el lenguaje, en lugar de contar todo lo que yo s. Pienso que hay una jerarqua de importancia de la informacin y que existen hechos que el 95% de los programadores nunca conocern, detalles que slo sirven para confundir a las personas y que incrementan su percepcin de la complejidad del lenguaje. Tomemos un ejemplo de Cf si se memoriza la tabla de precedencia de lo

24 Piensa en Java

soperadores (yo nunca lo he hecho), se puede escribir cdigo inteligente. Pero si se piensa en ello, tambin confundir la lectura y el mantenimiento de dicho cdigo, por tanto, hay que olvidarse de la precedencia y emplear parntesis cuando las cosas no estn claras.

3.

Mantener cada seccin enfocada de manera que el tiempo de lectura y el tiempo entre ejercicios, sea pequeo. Esto no slo mantiene las mentes de los alumnos ms activas cuando se est en un seminario, sino que tambin proporciona al lector una mayor sensacin de estar avanzando.

4.

Proporcionar al alumno una base slida de modo que pueda comprender los temas los suficientemente bien como para que desee acudir a cursos o libros ms avanzados.

Ensear con este libro

La edicin original de este libro ha evolucionado a partir de un seminario de una semana que era, cuando Java se encontraba en su infancia, suficiente tiempo para cubrir el lenguaje. A medida que Java fue creciendo y aadiendo ms y ms funcionalidades y bibliotecas, yo tenazmente trataba de ensearlo todo en una semana. En una ocasin, un cliente me sugiri que enseara slo los fundamentos y al hacerlo descubr que tratar de memorizar todo en una nica semana era angustioso tanto para m como para las personas que asistan al seminario. Java ya no era un lenguaje 'simple" que se poda aprender en una semana.

Dicha experiencia me llev a reorganizar este libro, el cual ahora est diseado como material de apoyo para un seminario de dos semanas o un curso escolar de dos trimestres. La parte de introduccin termina con el Capitulo 12, Tratamiento de errores mediante excepciones, aunque tambin puede complementarla con una introduccin a JDBC, Servlcts y JSP. Esto proporciona las bases y es el ncleo del CD-ROM Hands-On Java. El resto del libro se corresponde con un curso de nivel intermedio y es el material cubierto en el CDROM Intermediare Thinking in Java. Ambos discos CD ROM pueden adquirirse a tTavs de www.MindView.net.

Contacte con Prentice-Hall en www.prenhaHprofessional.com para obtener ms informacin acerca del material para el profesor relacionado con este libro. Documentacin del JDK en HTML

El lenguaje Java y las bibliotecas de Sun Microsystems (descarga gratuita en http://java.sun.com) se suministran con documentacin en formato electrnico, que se puede leer con un explorador web. Muchos de los libros publicados sobre Java proporcionan esta documentacin. Por tanto, o ya se tiene o puede descargase y, a menos que sea necesario, en este libro no se incluye dicha documentacin, porque normalmente es mucho ms rpido encontrar las descripciones de las clases en el explorador web que buscarlas en un libro (y probablemente la documentacin en lnea estar ms actualizada). Basta con que

Introduccin 25

utilice la referencia JDK documentaron. En este libro se proporcionan descripciones adicionales de las clases slo cuando es necesario complementar dicha documentacin, con el fin de que se pueda comprender un determinado ejemplo. Ejercicios

He descubierto que durante las clases los ejercicios sencillos son excepcionalmcntc tiles para alumno termine

que

el

comprender el tema, por lo que he incluido al final de cada captulo una serie de ejercicios.

La mayor parte de los ejercicios son bastante sencillos y estn diseados para que se puedan realizar durante un tiempo razonable de la clase, mientras el profesor observa los progresos, asegurndose de que los estudiantes aprenden el tema. Algunos son algo ms complejos, pero ninguno presenta un reto inalcanzable.

Las soluciones a los ejercicios seleccionados se pueden encontrar en el documento electrnicoTheThinking in Java Annotated So/ution Guide, que se puede adquirir en www.MindView.net. Fundamentos para Java

Otra ventaja que presenta esta edicin es el seminario multimedia gratuito que puede descargarse en la direccin www.MindView.net. Se trata del seminario Thinking in C. el cual proporciona una introduccin a los operadores, funciones y la sintaxis de C en la que se basa la sintaxis de Java. En las ediciones anteriores del libro se encontraba en el CD Foundations for Java que se proporcionaba junto con el libro, pero ahora este seminario puede descargarse gratuitamente.

Originalmente, encargu a Chuck Allison que creara Thinking in C como un producto autnomo, pero decid incluirlo en la segunda edicin de Thinking in C++ y en la segunda y tercera ediciones de Thinking in Java, por la experiencia de haber estado con personas que llegan a los seminarios sin tener una adecuada formacin en la sintaxis bsica deC. El razonamiento suele ser: Soy un programador inteligente y no quiero aprender C. sino C++ o Java, por tanto, me salto el C y paso directamente a ver el C++/Java\ Despus de asistir al seminario, lentamente todo el mundo se da cuenta de que el prerrequisito de conocer la sintaxis de C tiene sus buenas razones de ser.

Las tecnologas han cambiado y han permitido rehacer Thinking in C como una presentacin Flash

26 Piensa en Java

descargable en lugar de tener que proporcionarlo en CD. Al proporcionar este seminario en linea, puedo garantizar que todo el mundo pueda comenzar con una adecuada preparacin.

El seminario Thinking in C tambin permite atraer hacia el libro a una audiencia importante. Incluso aunque los captulos dedicados a operadores y al control de la ejecucin cubren las partes fundamentales de Java que proceden de C, el seminario en lnea es una buena introduccin y precisa del estudiante menos conocimientos previos sobre programacin que este libro.

Cdigo fuente

Todo el cdigo fuente de este libro est disponible graftiitamente y sometido a copyright, distribuido como un paquete nico, visitando el sitio web www.MindView.net. Para asegurarse de que obtiene la versin ms actual, ste es el sitio oficial de distribucin del cdigo. Puede distribuir el cdigo en las clases y en cualquier otra situacin educativa.

El objetivo principal del copyright es asegurar que el cdigo fuente se cite apropiadamente y evitar as que otros lo publiquen sin permiso. No obstante, mientras se cite la ftiente, no constituye ningn problema en la mayora de los medios que se empleen los ejemplos del libro.

En cada archivo de cdigo fuente se encontrar una referencia a la siguiente nota de copyright'. //:! Copyright.txt This computer source code is Copyright 2006 MindView, Inc. All Rights Reserved. Permission to use, copy, modify, and distribute this computer source code (Source Code) and its documentation without fee and without a written agreement for the purposes set forth below is hereby granted, provided that the above copyright notice, this paragraph and the following five numbered paragraphs appear in all copies. Permission is granted to compile the Source Code and to include the compiled code, in executable format only, in personal and commercial software programs.
1.

Permission is granted to use the Source Code without modification in classroom situations, including in presentation materials, provided that the book "Thinking in Java" is cited as the origin.
2.

Introduccin 27

Permission to incorporate the Source Code into printed media may be obtained by contacting:
3.

MindView, Inc. 5343 Valle Vista La Mesa, California 91941 Wayne@MindView.net The Source Code and documentation are copyrighted by MindView, Inc. The Source code is provided without express
4.

or implied warranty of any kind, including any implied warranty of merchantability, fitness for a particular purpose or non-infringement. MindView, Inc. does not warrant that the operation of any program that includes the Source Code will be uninterrupted or error-free. MindView, Inc. makes no representation about the suitability of the Source Code or of any software that includes the Source Code for any purpose. The entire risk as to the quality and performance of any program that includes the Source Code is with the user of the Source Code. The user understands that the Source Code was developed for research and instructional purposes and is advised not to rely exclusively for any reason on the Source Code or any program that includes the Source Code. Should the Source Code or any resulting software prove defective, the user assumes the cost of all necessary servicing, repair, or correction. 5. IN NO EVENT SHALL MINDVIEW, INC., OR ITS PUBLISHER BE LIABLE TO ANY PARTY UNDER ANY LEGAL THEORY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS, OR FOR PERSONAL INJURIES, ARISING OUT OF THE USE OF THIS SOURCE CODE AND ITS DOCUMENTATION, OR ARISING OUT OF THE INABILITY TO USE ANY RESULTING PROGRAM, EVEN IF MINDVIEW, INC., OR ITS PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MINDVIEW, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOURCE CODE AND DOCUMENTATION PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, WITHOUT ANY ACCOMPANYING SERVICES FROM MINDVIEW, INC., AND MINDVIEW, INC. HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. Please note that MindView, Inc. maintains a Web site which is the sole distribution point for electronic copies of the Source Code, http://www.MindView.net (and official mirror sites), where it is freely available under the terms stated above. If you think you've found an error in the Source Code, please submit a correction using the feedback system that you will find

28 Piensa en Java

at http://www.MindView.net.

///:

Puede utilizar el cdigo en sus proyectos y en la clase (incluyendo su material de presentaciones) siempre y cuando se mantenga la nota de copyright en cada uno de los archivos fuente. Estndares de codificacin

En el texto del libro, los identificadores (nombres de mtodos, variables y clases) se escriben en negrita. La mayora de las palabras clave se escriben en negrita, excepto aquellas palabras clave que se usan con mucha frecuencia y ponerlas en negrita podra volverse tedioso, como en el caso de la palabra class.

En este libro, he utilizado un estilo de codificacin particular para los ejemplos. Este estilo sigue el que emplea Sun en prcticamente todo el cdigo que encontrar en su sitio (vase http://java.sun.com/docs/codeconv/wdex.htnii), y que parece que soporta la mayora de los entornos de desarrollo Java. Si ha ledo mis otros libros, observar tambin que el estilo de codificacin de Sun coincide con el mo, lo que me complace, ya que yo 110 tengo nada que ver con la creacin del estilo de

Sun. El tema del estilo de formato es bueno para conseguir horas de intenso debate, por lo que no voy a intentar dictar un estilo correcto a travs de mis ejemplos; tengo mis propias motivaciones para usar el estilo que uso. Dado que Java es un lenguaje de programacin de formato libre, se puede emplear el estilo con el que uno se encuentre a gusto. Una solucin para el tema del estilo de codificacin consiste en utilizar una herramienta como Jalopy (www.triemax.com), la cual me ha ayudado en el desarrollo de este libro a cambiar el formato al que se adaptaba a mi.

Los archivos de cdigo impresos en el libro se han probado con un sistema automatizado, por lo que deberan ejecutarse sin errores de compilacin.

Este libro est basado y se ha comprobado con Java SE5/6. Si necesita obtener informacin sobre versiones anteriores del lenguaje que no se cubren en esta edicin, la ediciones primera y tercera del mismo pueden descargarse gratuitamente en www. MindView. net.

Introduccin 29

Errores

No importa cuantas herramientas utilice un escritor para detectar los errores, algunos quedan ah y a menudo son lo que primero ve el lector. Si descubre cualquier cosa que piensa que es un error, por favor utilice el vnculo que encontrar para este libro en www.MindView.net y enveme el error junto con la correccin que usted crea. Cualquier ayuda siempre es bienvenida.Introduccin a los objetos

Analizamos la Naturaleza, la organizamos en conceptos y vamos asignando significados a medida que lo hacemos, fundamentalmente porque participamos en un acuerdo tcito suscrito por toda nuestra comunidad de hablantes y que est codificado en los propios patrones de nuestro idioma... nos resulta imposible hablar si no utilizamos la organizacin y clasificacin de los

datos decretadas por ese acuerdo". Benjamn Lee Whorf (1897-1941)

La gnesis de la revolucin de las computadoras se hallaba en una mquina. La gnesis de nuestros lenguajes de programacin tiende entonces a parecerse a dicha mquina.

Pero las computadoras, ms que mquinas, pueden considerarse como herramientas que permiten ampliar la mente (bicicletas para la mente, como se enorgullece en decir Steve Jobs), adems de un medio de expresin diferente. Como resultado, las herramientas empiezan a parecerse menos a

mquinas y ms a panes de nuestras mentes, al igual que ocurre con otras formas de expresin como la escritura, la pintura, la escultura, la animacin y la realizacin de pelculas. La programacin orientada a objetos (POO) es parte de este movimiento dirigido al uso de las computadoras como un medio de expresin.

Este captulo presenta los conceptos bsicos de la programacin orientada a objetos, incluyendo una introduccin a los mtodos de desarrollo. Este captulo, y este libro, supone que el lector tiene cierta experiencia en programacin, aunque no necesariamente en C. Si cree cjue necesita una mayor preparacin antes de abordar este libro, debera trabajar con el seminario multimedia sobre C, Thinking in C, que puede descargarse en MindView.net.

Este captulo contiene material de carcter general y suplementario. Muchas personas pueden no sentirse cmodas si se enfrentan a la programacin orientada a objetos sin obtener primero una visin general. Por tanto, aqu se presentan muchos conceptos que proporcionarn una slida introduccin a la POO. Sin embargo, otras personas pueden no necesitar tener una visin general hasta haber visto algunos de los mecanismos primero, estas personas suelen perderse si no se les ofrece algo de cdigo que puedan manipular. Si usted forma parte de este ltimo grupo, estar ansioso por ver las especifidades del lenguaje, por lo que puede saltarse este capitulo, esto no le impedir aprender a escribir programas ni conocer el lenguaje. Sin embargo, podr volver aqu cuando lo necesite para completar sus conocimientos, con el fin de comprender por qu son importantes los objetos y cmo puede disearse con ellos. El progreso de la abstraccin

Todos los lenguajes de programacin proporcionan abstracciones. Puede argumentarse que la complejidad de los problemas que sea capaz de resolver est directamente relacionada con el tipo (clase) y la calidad de las abstracciones, entendiendo por clase, qu es lo que se va a abstraer? El lenguaje ensamblador es una pequea abstraccin de la mquina subyacente. Muchos de los lenguajes denominados imperativos que le siguieron (como FORTRAN, BASIC y C) fueron abstracciones del lenguaje ensamblador. Estos lenguajes constituyen grandes mejoras sobre el lenguaje ensamblador, pero su principal abstraccin requiere que se piense en trminos de la estructura de la computadora en

lugar de en la estructura del problema que se est intentado resolver. El programador debe establecer la asociacin entre el modelo de la mquina (en el espacio de la solucin, que es donde se va a implementar dicha solucin, como puede ser una computadora) y el modelo

del problema que es lo que realmente se quiere resolver (en el espacio del problema, que es el lugar donde existe el problema. como por ejemplo en un negocio). El esfuerzo que se requiere para establecer esta correspondencia y el hecho de que sea extrnseco al lenguaje de programacin, da lugar a programas que son difciles de escribir y caros de mantener, adems del efecto colateral de toda una industria de mtodos de programacin.

La alternativa a modelar la maquina es modelar el problema que se est intentado solucionar. Los primeros lenguajes como L1SP y APL eligen vistas parciales del mundo (todos los problemas pueden reducirse a listas o todos los problemas son algortmicos, respectivamente). Prolog convierte todos los problemas en cadenas de decisin. Los lenguajes se han creado para programar basndose en restricciones y para programar de forma exclusiva manipulando smbolos grficos (aunque se demostr que este caso era demasiado restrictivo). Cada uno de estos mtodos puede ser una buena solucin para resolver la clase de problema concreto para el que estn diseados, pero cuando se aplican en otro dominio resultan inadecuados.

El enfoque orientado a objetos trata de ir un paso ms all proporcionando herramientas al programador para representar los elementos en el espacio del problema. Esta representacin es tan general que el programador no est restringido a ningn tipo de problema en particular. Se hace referencia a los elementos en el espacio del problema denominando objetos a sus representaciones en el espacio de la solucin (tambin se necesitarn otros objetos que no tendrn anlogos en el espacio del problema). La idea es que el programa pueda adaptarse por s slo a la jerga del problema aadiendo nuevos tipos de objetos, de modo que cuando se lea el cdigo que describe la solucin, se estn leyendo palabras que tambin expresen el problema. Esta es una abstraccin del lenguaje ms flexible y potente que cualquiera de las que se hayan hecho anteriormentec. Por tanto, la programacin orientada a objetos permite describir el problema en trminos del problema en lugar de en trminos de la computadora en la que se ejecutar la solucin. Pero an existe una conexin con la computadora, ya que cada objeto es similar a una pequea computadora (tiene un estado y dispone de operaciones que el programador puede pedirle que realice). Sin embargo, esto no quiere decir que nos encontremos ante una mala analoga de los objetos del mundo real, que tienen caractersticas y comportamientos.

Algunos diseadores de lenguajes han decidido que la programacin orientada a objetos por s misma no es adecuada para resolver fcilmente todos los problemas de la programacin, y recomiendan combinar varios mtodos en lenguajes de programacin multiparadigma. Consulte Sfultiparadim Programming in Leda de Timothy Budd (Addison-Wcsicy, 1995).
c

Alan Kay resumi las cinco caractersticas bsicas del Smalltalk. el primer lenguaje orientado a objetos que tuvo xito y uno de los lenguajes en los que se basa Java. Estos caractersticas representan un enfoque puro de la programacin orientada a objetos.

1.

Iodo es un objeto. Piense en un objeto como en una variable: almacena datos, permite que se le planteen solicitudes, pidindole que realice operaciones sobre s mismo. En teora, puede tomarse cualquier componente conceptual del problema que se est intentado resolver (perros, edificios, servicios, etc.) y representarse como un objeto del programa. Un programa es un montn de objetos que se dicen entre s lo que tienen que hacer envindose mensajes.

2.

Para hacer una solicitud a un objeto, hay que enviar un mensaje a dicho objeto. Ms concretamente, puede pensar en que un mensaje es una solicitud para llamar a un mtodo que pertenece a un determinado objeto.

3.

Cada objeto tiene su propia memoria formada por otros objetos. Dicho de otra manera, puede crear una nueva clase de objeto definiendo un paquete que contenga objetos existentes. Por tanto, se puede incrementar la complejidad de un programa ocultndola tras la simplicidad de los objetos.

4.

Todo objeto tiene un tipo asociado. Como se dice popularmente, cada objeto es una instancia de una clase, siendo clase sinnimo de tipo. La caracterstica distintiva ms importante de una clase es el conjunto de mensajes que se le pueden enviar.

5.

Todos los objetos de un tipo particular pueden recibir los mismos mensajes. Como veremos ms adelante, esta afirmacin es realmente importante. Puesto que un objeto de tipo crculo tambin es un objeto de tipo forma, puede garantizarse que un crculo aceptar los mensajes de forma. Esto quiere decir que se puede escribir cdigo para comunicarse con objetos de tipo forma y controlar automticamente cualquier cosa que se ajuste a la descripcin de una forma. Esta capacidad de stiplantacin es uno de los conceptos ms importantes de la programacin orientada a objetos.

Booch ofrece una descripcin an ms sucinta de objeto: Un objeto tiene estado, comportamiento e identidad.

Esto significa que un objeto puede tener datos internos (lo que le proporciona el estado), mtodos (para proporcionar un comportamiento) y que cada objeto puede ser diferenciado de forma unvoca de cualquier otro objeto; es decir, cada objeto tiene una direccin de memoria exclusiva.-2 Todo objeto tiene una interfaz

Aristteles fue probablemente el primero en estudiar cuidadosamente el concepto de tipo\ hablaba de la clase de peces y de la clase de pjaros. La idea de que todos los objetos, an siendo nicos, son tambin parte de una clase de objetos que tienen caractersticas y comportamientos comunes ya se emple en el primer lenguaje orientado a objetos, el Simula-67, que ya usaba su palabra clave fundamental class, que permite introducir un nuevo tipo en un programa.

Algunas personas hacen una distincin, estableciendo que el lipo determina la interfaz mientras que la clase es una implcmcntacin concreta de dicha interfaz.
2

Simula, como su nombre implica, se cre para desarrollar simulaciones como la clsica del problema del cajero de un banco. En esta simulacin, se tienen muchos cajeros, clientes, cuentas, transacciones y unidades monetarias, muchsimos objetos. Los objetos, que son idnticos excepto por su estado durante la ejecucin de un programa, se agrupan en clases de objetos, que es de donde procede la palabra clave class. La creacin de tipos de datos abstractos (clases) es un concepto fundamental en la programacin orientada a objetos. Los tipos de datos abstractos funcionan casi exactamente como tipos predefinidos: pueden crearse variables de un tipo (llamadas objetos u instancias en la jerga de la POO) y manipular dichas variables (mediante el envo de mensajes o solicitudes, se enva un mensaje y el objeto sabe lo que tiene que hacer con l). Los miembros (elementos) de cada clase comparten algunos rasgos comunes. Cada cuenta tiene asociado un saldo, cada cajero puede aceptar un depsito, etc. Adems, cada miembro tiene su propio estado. Cada cuenta tiene un saldo diferente y cada cajero tiene un nombre. Por tanto, los cajeros, clientes, cuentas, transacciones, etc., pueden representarse mediante una entidad unvoca en el programa informtico. Esta entidad es el objeto y cada objeto pertenece a una determinada clase que define sus caractersticas y comportamientos.

Por tanto, aunque en la programacin orientada a objetos lo que realmente se hace es crear nuevos tipos de datos, en la prctica, todos los lenguajes de programacin orientada a objetos utilizan la palabra clave class. Cuando vea la palabra type (tipo) piense en class (clase), y viceversa.'

Dado que una clase describe un conjunto de objetos que tienen caractersticas (elementos de datos) y comportamientos (funcionalidad) idnticos, una clase realmente es un tipo de datos porque, por ejemplo, un nmero en coma flotante tambin tiene un conjunto de caractersticas y comportamientos. La diferencia est en que el programador define un clase para adaptar un problema en lugar de forzar el uso de un tipo de datos existente que fue diseado para representar una unidad de almacenamiento en una mquina. Se puede ampliar el lenguaje de programacin aadiendo nuevos tipos de datos especficos que se adapten a sus necesidades. El sistema de programacin admite las nuevas clases y proporciona a todas ellas las comprobaciones de tipo que proporciona a los tipos predefinidos.

El enfoque orientado a objetos no est limitado a la creacin de simulaciones. Se est o no de acuerdo en que cualquier programa es una simulacin del sistema que se est diseando, el uso de las tcnicas de la POO puede reducir fcilmente un gran conjunto de problemas a una sencilla solucin.

Una vez que se ha definido una clase, se pueden crear tantos objetos de dicha clase como se desee y dichos objetos pueden manipularse como si fueran los elementos del problema que se est intentado resolver. Realmente, uno de los retos de la programacin orientada a objetos es crear una correspondencia uno-a-uno entre los elementos del espacio del problema y los objetos del espacio de la solucin.

Pero, cmo se consigue que un objeto haga un trabajo til para el programador? Debe haber una forma de hacer una solicitud al objeto para que haga algo, como por ejemplo, completar una transaccin, dibujar algo en pantalla o encender un interruptor. Adems, cada objeto slo puede satisfacer ciertas solicitudes. Las solicitudes que se pueden hacer a un objeto se definen mediante su interfaz y es el tipo lo que determina la interfaz. Veamos un ejemplo con la representacin de una bombilla:

Tipo

Interfaz

Luz lz = new Luz(); 1z.encender{);

La interfaz determina las solicitudes que se pueden hacer a un determinado objeto, por lo que debe existir un cdigo en alguna parte que satisfaga dicha solicitud. Esto, junto con los datos ocultos, definen lo que denomina la implementacin. Desde el punto de vista de la programacin proced mental, esto no es complicado. Un tipo tiene un mtodo asociado con cada posible solicitud; cuando se hace una determinada solicitud a un objeto, se llama a dicho mtodo. Este proceso se resume diciendo que el programador enva un mensaje (hace una solicitud) a un objeto y el objeto sabe lo que tiene que hacer con ese mensaje (ejecuta el cdigo).

En este ejemplo, el nombre del tipo/clase es Luz. el nombre de este objeto concreto Luz es lz y las solicitudes que se pueden hacer a un objeto Luz son encender, apagar, brillar o atenuar. Se ha creado un objeto Luz definiendo una referencia (lz) para dicho objeto e invocando new para hacer una solicitud a un nuevo objeto de dicho tipo. Para enviar un mensaje al objeto, se define el nombre del

objeto y se relaciona con la solicitud del mensaje mediante un punto. Desde el punto de vista del usuario de una clase predefinida, esto es el no va ms de la programacin con objetos.

El diagrama anterior sigue el formato del lenguaje UML (Unified Modeling Language, lenguaje de modelado unificado). Cada clase se representa mediante un recuadro escribiendo el nombre del tipo en la parte superior, los miembros de datos en la zona intermedia y los mtodos (las funciones de dicho objeto que reciben cualquier mensaje que el programador enve a dicho objeto) en la parte inferior. A menudo, en estos diagramas slo se muestran el nombre de la clase y los mtodos pblicos, no incluyndose la zona intermedia, como en este caso. Si slo se est interesado en el nombre de la clase, tampoco es necesario incluir la parte inferior. Un objeto proporciona servicios

Cuando se est intentando desarrollar o comprender el diseo de un programa, una de las mejores formas de pensar en los objetos es como si fueran proveedores de servicios. El programa proporciona servicios al usuario y esto se conseguir utilizando los servicios que ofrecen otros objetos. El objetivo es producir (o incluso mejor, localizar en las bibliotecas de cdigo existentes) un conjunto de objetos que facilite los servicios idneos para resolver el problema.

Una manera de empezar a hacer esto es preguntndose: Si pudiera sacarlos de un sombrero mgico, qu objetos resolveran el problema de la forma ms simple?. Por ejemplo, suponga que quiere escribir un programa de contabilidad. Puede pensar en algunos objetos que contengan pantallas predefinidas para la introduccin de los datos contables, otro conjunto de objetos que realicen los clculos necesarios y un objeto que controle la impresin de los cheques y las facturas en toda clase de impresoras. Es posible que algunos de estos objetos ya existan, pero cmo deben ser los que no existen? Qu servicios deberan proporcionar esos objetos y qu objetos necesitaran para cumplir con sus obligaciones? Si se hace este planteamiento, llegar a un punto donde puede decir: Este objeto es lo suficientemente sencillo como para escribirlo yo mismo o Estoy seguro de que este objeto ya tiene que existir. sta es una forma razonable de descomponer un problema en un conjunto de objetos.

Pensar en un objeto como en un proveedor de servicios tiene una ventaja adicional: ayuda a mejorar la cohesin del objeto. Una alta cohesin es una cualidad fundamental del diseo software, lo que significa que los diferentes aspectos de un componente de software (tal como un objeto, aunque tambin podra aplicarse a un mtodo o a una biblioteca de objetos) deben ajustar bien entre si. Un problema que suelen tener los programadores cuando disean objetos es el de asignar demasiada funcionalidad al objeto. Por ejemplo, en el mdulo para imprimir cheques, puede decidir que es necesario un objeto que sepa todo sobre cmo dar formato c imprimir. Probablemente, descubrir que esto es demasiado para un solo objeto y que hay que emplear tres o ms objetos. Un objeto puede ser un catlogo de todos los posibles diseos de cheque, al cual se le

puede consultar para obtener informacin sobre cmo imprimir un cheque. Otro objeto o conjunto de objetos puede ser una interfaz de impresin genrica que sepa todo sobre las diferentes clases de impresoras (pero nada sobre contabilidad; por ello, probablemente es un candidato para ser comprado en lugar de escribirlo uno mismo). Y un tercer objeto podra utilizar los servicios de los otros dos para llevar a cabo su tarea. Por tanto, cada objeto tiene un conjunto cohesivo de servicios que ofrecer. En un buen diseo orientado a objetos, cada objeto hace una cosa bien sin intentar hacer demasiadas cosas. Esto adems de permitir descubrir objetos que pueden adquirirse (el objeto interfaz de impresora), tambin genera nuevos objetos que se reutilizarn en otros diseos.

Tratar los objetos como proveedores de servicios es una herramienta que simplifica mucho. No slo es til durante el proceso de diseo, sino tambin cuando alguien intenta comprender su propio cdigo o reutilizar un objeto. Si se es capaz de ver el valor del objeto basndose en el servicio que proporciona, ser mucho ms fcil adaptarlo al diseo. La implementacin oculta

Resulta til descomponer el campo de juego en creadores de clases (aquellos que crean nuevos tipos de datos) y en programadores de clientes3 (los consumidores de clases que emplean los tipos de datos en sus aplicaciones). El objetivo del programador cliente es recopilar una caja de herramientas completa de clases que usar para el desarrollo rpido de aplicaciones. El objetivo del creador de clases es construir una clase que exponga al programador cliente slo lo que es necesario y mantenga todo lo dems oculto. Por qu? Porque si est oculto, el programador cliente no puede acceder a ello, lo que significa que el creador de clases puede cambiar la parte oculta a voluntad sin preocuparse del impacto que la modificacin pueda implicar. Normalmente, la parte oculta representa las vulnerabilidades internas de un objeto que un programador cliente poco cuidadoso o poco formado podra corromper fcilmente, por lo que ocultar la implementacin reduce los errores en los programas.

En cualquier relacin es importante tener lmites que todas las partes implicadas tengan que respetar. Cuando se crea una biblioteca, se establece una relacin con el programador de clientes, que tambin 3 Termino acuado por rnt amigo Scott Meyers.

es un programador, pero que debe construir su aplicacin utilizando su biblioteca, posiblemente con el fin de obtener una biblioteca ms grande. Si todos los miembros de una clase estn disponibles para cualquiera, entonces el programador de clientes puede hacer cualquier cosa con dicha clase y no hay forma de imponer reglas. Incluso cuando prefiera que el programador de clientes no manipule directamente algunos de los miembros de su clase, sin control de acceso no hay manera de impedirlo. Todo est a la vista del mundo.

Por tanto, la primera razn que justifica el control de acceso es mantener las manos de los programadores cliente apartadas de las partes que son necesarias para la operacin interna de los tipos de datos, pero no de la parte correspondiente a la interfaz que los usuarios necesitan para resolver sus problemas concretos. Realmente, es un servicio para los programadores de clientes porque pueden ver fcilmente lo que es importante para ellos y lo que pueden ignorar.

La segunda razn del control de acceso es permitir al diseador de bibliotecas cambiar el funcionamiento interno de la clase sin preocuparse de cmo afectar al programador de clientes. Por ejemplo, desea implementar una clase particular de una forma sencilla para facilitar el desarrollo y ms tarde descubre que tiene que volver a escribirlo para que se ejecute ms rpidamente. Si la interfaz y la implementacin estn claramente separadas y protegidas, podr hacer esto fcilmente.

Java emplea tres palabras clave explcitamente para definir los lmites en una clase: public, prvate y protected Estos modificadores de acceso determinan quin puede usar las definiciones del modo siguiente: public indica que el elemento que le sigue est disponible para todo el mundo. Por otro lado, la palabra clave private, quiere decir que nadie puede acceder a dicho elemento excepto usted, el creador del tipo, dentro de los mtodos de dicho tipo, prvate es un muro de ladrillos entre usted y el programador de clientes. Si alguien intenta acceder a un miembro private obtendr un error en tiempo de compilacin. La palabra clave protected acta como private, con la excepcin de que una clase heredada tiene acceso a los miembros protegidos (protected), pero no a los privados (private). Veremos los temas sobre herencia enseguida.

Java tambin tiene un acceso predeterminado, que se emplea cuando no se aplica uno de los modificadores anteriores. Normalmente, esto se denomina acceso de paquete, ya que las clases pueden acceder a los miembros de otras clases que pertenecen al mismo paquete (componente de biblioteca), aunque fuera del paquete dichos miembros aparecen como privados (prvate). Reutilizacin de la implementacin

Una vez que se ha creado y probado una clase, idealmente debera representar una unidad de cdigo til. Pero esta reutilizacin no siempre es tan fcil de conseguir como era de esperar; se necesita experiencia y perspicacia para generar un diseo de un objeto reutilizable. Pero, una vez que se dispone de tal diseo, parece implorar ser reutilizado. La reutilizacin de cdigo es una de las grandes ventajas que proporcionan los lenguajes de programacin orientada a objetos.

La forma ms sencilla de reutilizar una clase consiste simplemente en emplear directamente un objeto de dicha clase, aunque tambin se puede colocar un objeto de dicha clase dentro de una clase nueva. Esto es lo que se denomina crear un objeto miembro. La nueva clase puede estar formada por cualquier nmero y tipo de otros objetos en cualquier combinacin necesaria para conseguir la funcionalidad deseada en dicha nueva clase. Definir una nueva clase a partir de clases existentes se denomina composicin (si la composicin se realiza de forma dinmica, se llama agregacin). A menudo se hace referencia a la composicin como una relacin "tiene un", como en un coche tiene un motor. Este diagrama UML indica la composicin mediante un rombo relleno, que establece que hay un coche. Normalmente, yo utilizo una forma ms sencilla: slo una linea, sin el rombo, para indicar una asociacin.4

La composicin conlleva una gran flexibilidad. Los objetos miembro de la nueva clase normalmente Normalmente, es suficiente grado de detalle para la mayora de los diagramas y no es necesario especificar si se est usando una agregacin o una composicin.
4

son privados, lo que Ies hace inaccesibles a los programadores de clientes que estn usando la clase. Esto le permite cambiar dichos miembros sin disturbar al cdigo cliente existente. Los objetos miembro tambin se pueden modificar en tiempo de ejecucin, con el fin de cambiar dinmicamente el comportamiento del programa. La herencia, que se describe a continuacin, no proporciona esta flexibilidad, ya que el compilador tiene que aplicar las restricciones en tiempo de compilacin a las clases creadas por herencia.

Dado que la herencia es tan importante en la programacin orientada a objetos, casi siempre se enfatiza mucho su uso. de manera que los programadores novatos pueden llegar a pensar que hay que emplearla en todas partes. Esto puede dar lugar a que se hagan diseos demasiado complejos y complicados. En lugar de esto, en primer lugar, cuando se van a crear nuevas clases debe considerarse la composicin, ya que es ms simple y flexible. Si aplica este mtodo, sus diseos sern ms inteligentes. Una vez que haya adquirido algo de experiencia, ser razonablemente obvio cundo se necesita emplear la herencia. Herencia

Por s misma, la idea de objeto es una buena herramienta. Permite unir datos y funcionalidad por concepto, lo que permite representar la idea del problema-espacio apropiada en lugar de forzar el uso de los idiomas de la mquina subyacente. Estos conceptos se expresan como unidades fundamentales en el lenguaje de programacin utilizando la palabra clave class.

Sin embargo, es una pena abordar todo el problema para crear una clase y luego verse forzado a crear una clase nueva que podra tener una funcionalidad similar. Es mejor, si se puede, tomar la clase existente, clonarla y luego aadir o modificar lo que sea necesario al clon. Esto es lo que se logra con la herencia, con la excepcin de que la clase original (llamada clase base, superclase o clase padre) se modifica, el clon "modificado" (denominado clase derivada, clase heredada, subclase o clase hija) tambin refleja los cambios. base

t derivada

La flecha de este diagrama UML apunta de la clase derivada a la clase base. Como veremos, puede haber ms de una clase derivada.

Un tipo hace ms que describir las restricciones definidas sobre un conjunto de objetos; tambin tiene una relacin con otros tipos. Dos tipos pueden tener caractersticas y comportamientos en comn, pero un tipo puede contener ms caractersticas que el otro y tambin es posible que pueda manejar ms mensajes (o manejarlos de forma diferente). La herencia expresa esta similitud entre tipos utilizando el concepto de tipos base y tipos derivados. Un tipo base contiene todas las caractersticas y comportamientos que los tipos derivados de l comparten. Es recomendable crear un tipo base para representar el ncleo de las ideas acerca de algunos de los objetos del sistema. A partir de ese tipo base, pueden deducirse otros tipos para expresar las diferentes formas de implementar ese ncleo.

Por ejemplo, una mquina para el reciclado de basura clasifica los desperdicios. El tipo base es basura y cada desperdicio tiene un peso, un valor, etc., y puede fragmentarse, mezclarse o descomponerse. A partir de esto, se derivan ms tipos especficos de basura que pueden tener caractersticas adicionales (una botella tendr un color) o comportamientos (el aluminio puede modelarse, el accro puede tener propiedades magnticas). Adems, algunos comportamientos pueden ser diferentes (el valor del papel depende de su tipo y condicin). Utilizando la herencia, puede construir una jerarqua de tipos que exprese el problema que est intentando resolver en trminos de sus tipos.

Un segundo ejemplo es el clsico ejemplo de la forma, quiz usado en los sistemas de diseo asistido por compradora o en la simulacin de juegos. El tipo base es forma y cada forma tiene un tamao, un color, una posicin, etc. Cada forma puede dibujarse, borrarse, desplazarse, colorearse, etc. A partir de esto, se derivan (heredan) los tipos especficos de formas (circulo, cuadrado, tringulo, etc.), cada una con sus propias caractersticas adicionales y comportamientos. Por ejemplo, ciertas formas podrn voltearse. Algunos comportamientos pueden ser diferentes, como por ejemplo cuando se quiere calcular su rea. La jerarqua de tipos engloba tanto las similitudes con las diferencias entre las formas.

Representar la solucin en los problema es muy til, porque modelos intermedios para problema a una descripcin de jerarqua de tipos es el puede pasar directamente de el mundo real a la descripcin A pesar de esto, una de las los programadores con el que es demasiado sencillo ir Una mente formada para ver inicialmente, verse simplicidad.

mismos trminos que el no se necesitan muchos pasar de una descripcin del la solucin. Con objetos, la modelo principal, porque se la descripcin del sistema en del sistema mediante cdigo. dificultades que suelen tener diseo orientado a objetos es del principio hasta el final. soluciones complejas puede, desconcertada por esta

Cuando se hereda de un tipo existente, se crea un tipo nuevo. Este tipo nuevo no slo contiene todos los miembros del tipo existente (aunque los privados estn ocultos y son inaccesibles), sino lo que es ms importante, duplica la interfaz de la clase base; es decir, todos los mensajes que se pueden enviar a los objetos de la clase base tambin se pueden enviar a los objetos de la clase derivada. Dado que conocemos el tipo de una clase por los mensajes que se le pueden enviar, esto quiere decir que la clase derivada es del mismo tipo que la dase base. En el ejemplo anterior, un crculo es una forma. Esta equivalencia de tipos a travs de la herencia es uno de los caminos fundamentales para comprender el significado de la programacin orientada a objetos.

Puesto que la clase base y la clase derivada tienen la misma interfaz, debe existir alguna implementacin que vaya junto con dicha interfaz. Es decir, debe disponerse de algn cdigo que se ejecute cuando un objeto recibe un mensaje concreto. Si simplemente hereda una clase y no hace nada ms. los mtodos de la interfaz de la clase base pasan tal cual a la clase derivada, lo que significa que los objetos de la clase derivada no slo tienen el mismo tipo sino que tambin tienen el mismo comportamiento, lo que no es especialmente interesante.

Hay dos formas de diferenciar la nueva clase derivada de la clase base original. La primera es bastante directa: simplemente, se aaden mtodos nuevos a la clase derivada. Estos mtodos nuevos no forman parte de la interfaz de la clase base, lo que significa que sta simplemente no haca todo lo que se necesitaba y se le han aadido ms mtodos. Este sencillo y primitivo uso de la herencia es, en ocasiones, la solucin perfecta del problema que se tiene entre manos. Sin embargo, debe considerarse siempre la posibilidad de que la clase base pueda tambin necesitar esos mtodos adicionales. Este proceso de descubrimiento e iteracin en un diseo tiene lugar habitualmente en la programacin orientada a objetos.

Aunque en ocasiones la (especialmente en Java, herencia es extends) que se nuevos a la interfaz, no necesariamente. La forma de diferenciar la el comportamiento de un clase base. Esto es lo que mtodo.

herencia puede implicar donde la palabra clave para van a aadir mtodos tiene que ser as segunda y ms importante nueva clase es cambiando mtodo existente de la se denomina sustitucin del

Para sustituir un mtodo, definicin para el mismo decir, se usa el mismo mtodo haga algo diferente en el tipo Relaciones es-un y es-

basta con crear una nueva en la clase derivada. Es de interfaz, pero se quiere que nuevo. como-un

Es habitual que la herencia suscite un pequeo debate: debe la herencia sustituir slo los mtodos de la clase base (y no aadir mtodos nuevos que no existen en la clase base)? Esto significara que la clase derivada es exactamente del mismo tipo que la clase base, ya que tiene exactamente la misma interfaz. Como resultado, es posible sustituir de forma exacta un objeto de la clase derivada por uno de la clase base. Se podra pensar que esto es una sustitucin pura y a menudo se denomina principio de sustitucin. En cierto sentido, sta es la forma ideal de tratar la herencia. A menudo, en este caso, la relacin entre la clase base y las clases derivadas se dice que es una relacin es-un, porque podemos decir, un crculo es una forma. Una manera de probar la herencia es determinando si se puede aplicar la relacin es-un entre las clases y tiene sentido.

A veces es necesario aadir nuevos elementos de interfaz a un tipo derivado, ampliando la interfaz. El tipo nuevo puede todava ser sustituido por el tipo base, pero la sustitucin no es perfecta porque el tipo base no puede acceder a los mtodos nuevos. Esto se describe como una relacin es-como-un. El tipo nuevo tiene la interfaz del tipo antiguo pero tambin contiene otros mtodos, por lo que realmente no se puede decir que sean exactos. Por ejemplo, considere un sistema de aire acondicionado. Suponga que su domicilio est equipado con todo el cableado para controlar el equipo, es decir, dispone de una interfaz que le permite controlar el aire fro. Imagine que el aparato de aire acondicionado se estropea y lo reemplaza por una bomba de calor, que puede generar tanto aire caliente como fro. La bomba de calor es-como-un aparato de aire acondicionado, pero tiene ms funciones. Debido a que el sistema de control de su casa est diseado slo para controlar el aire fro, est restringido a la comunicacin slo con el sistema de fro del nuevo objeto. La interfaz del nuevo objeto se ha ampliado y el sistema existente slo conoce la interfaz original.

Por supuesto, una vez que uno ve este diseo, est claro que la clase base sistema de aire acondicionado no es general y debera renombrarse como sistema de control de temperatura" con el fin de poder incluir tambin el control del aire caliente, en esta situacin, est claro que el principio de sustitucin funcionar. Sin embargo, este diagrama es un ejemplo de lo que puede ocurrir en el diseo en el mundo real.

Cuando se ve claro que el principio de sustitucin (la sustitucin pura) es la nica forma de poder hacer las cosas, debe aplicarse sin dudar. Sin embargo, habr veces que no estar tan claro y ser mejor aadir mtodos nuevos a la interfaz de una clase derivada. La experiencia le proporcionar los conocimientos necesarios para saber que mtodo emplear en cada caso.

Objetos intercambiables con polimorfismo

Cuando se trabaja con jerarquas de tipos, a menudo se desea tratar un objeto no como el tipo especfico que es. sino como su tipo base. Esto permite escribir cdigo que no dependa de tipos especficos. En el ejemplo de las formas, los mtodos manipulan las formas genricas, independientemente de que se trate de crculos, cuadrados, tringulos o cualquier otra forma que todava no haya sido definida. Todas las formas pueden dibujarse, borrarse y moverse, por lo que estos mtodos simplemente envan un mensaje a un objeto forma, sin preocuparse de cmo se enfrenta el objeto al mensaje.

Tal cdigo no se ve afectado por la adicin de tipos nuevos y esta adicin de tipos nuevos es la forma ms comn de ampliar un programa orientado a objetos para manejar situaciones nuevas. Por ejemplo, puede derivar un subtipo nuevo de forma llamado pentgono sin modificar los mtodos asociados slo con las formas genricas. Esta capacidad de ampliar fcilmente un diseo derivando nuevos subtipos es una de las principales formas de encapsular cambios. Esto mejora enormemente los diseos adems de reducir el coste del mantenimiento del software.

Sin embargo, existe un problema cuando se intenta tratar los objetos de tipos derivado como sus tipos base genricos (crculos como formas, bicicletas como automviles, cormoranes como aves, etc.). Si un mtodo dice a una forma que se dibuje, o a un automvil genrico que se ponga en marcha o a un ave que se mueva, el compilador no puede saber en tiempo de compilacin de forma precisa qu pane del cdigo tiene que ejecutar. ste es el punto clave, cuando se envia el mensaje, el programador no desea saber qu parte del cdigo se va a ejecutar; el mtodo para dibujar se puede aplicar igualmente a un circulo, a un cuadrado o a un tringulo y los objetos ejecutarn el cdigo apropiado dependiendo de su tipo especfico.

Si no se sabe qu fragmento de cdigo se ejecutar, entonces se aadir un subtipo nuevo y el cdigo que se ejecute puede ser diferente sin que sea necesario realizar cambios en el mtodo que lo llama. Por tanto, el compilador no puede saber de forma precisa qu fragmento de cdigo hay que ejecutar y qu hace entonces? Por ejemplo, en el siguiente diagrama, el objeto controladorAves slo funciona con los objetos genricos Ave y no sabe exactamente de qu tipo son. Desde la perspectiva del objeto controladorAves esto es adecuado ya que no tiene que escribir cdigo especial para determinar el tipo exacto de Ave con el que est trabajando ni el comportamiento de dicha Ave. Entonces, cmo es que cuando se invoca al mtodo moverO ignorando el tipo especfico de Ave. se ejecuta el comportamiento correcto (un Ganso camina, vuela o nada y un Pingino camina o nada)? La respuesta es una de las principales novedades de la programacin orientada a objetos: el compilador no puede hacer una llamada a funcin en el sentido tradicional. La llamada a funcin generada por un compilador no-POO hace lo que se denomina un acoplamiento temprano, trmino que es posible que no haya escuchado antes. Significa que el compilador genera una llamada a un nombre de funcin especfico y el sistema de tiempo de ejecucin resuelve esta llamada a la direccin absoluta del cdigo que se va a ejecutar. En la POO, el programa no puede determinar la direccin del cdigo hasta estar en tiempo de ejecucin, por lo que se hace necesario algn otro esquema cuando se enva un mensaje a un objeto genrico.

Para resolver el problema, los lenguajes orientados a objetos utilizan el concepto de acopiamiento tardo. Cuando se enva un mensaje a un objeto, el cdigo al que se est llamando no se determina hasta el tiempo de ejecucin. El compilador no asegura que el mtodo exista, realiza una comprobacin de tipos con los argumentos y devuelve un valor, pero no sabe exactamente qu cdigo tiene que ejecutar.

Para realizar el acoplamiento tardo, Java emplea un bit de cdigo especial en lugar de una llamada absoluta. Este cdigo calcula la direccin del cuerpo del mtodo, utilizando la informacin almacenada en el objeto (este proceso se estudia en detalle en el Captulo 8, Polimorfismo). Por tanto, cada objeto puede comportarse de forma diferente de acuerdo con los contenidos de dicho bit de cdigo especial. Cuando se enva un mensaje a un objeto, realmente el objeto resuelve lo que tiene que hacer con dicho mensaje.

En algunos lenguajes debe establecerse explcitamente que un mtodo tenga la flexibilidad de las propiedades del acoplamiento tardo (C++ utiliza la palabra clave virtual para ello). En estos lenguajes, de manera predeterminada, los mtodos no se acoplan de forma dinmica. En Java, el acoplamiento dinmico es el comportamiento predeterminado y el programador no tiene que aadir ninguna palabra clave adicional para definir el polimorfismo.

Considere el ejemplo de las formas. La familia de clases (todas basadas en la misma interfaz uniforme) se ha mostrado en un diagrama anteriormente en el captulo. Para demostrar el polimorfismo, queremos escribir un fragmento de cdigo que ignore los detalles especficos del tipo y que slo sea indicado para la clase base. Dicho cdigo se desacopla de la informacin especfica del tipo y por tanto es ms sencillo de escribir y de comprender. Y, por ejemplo, si se aade un tipo nuevo como Hexgono a travs de la herencia, el cdigo que haya escrito funcionar tanto para el nuevo tipo de Forma como para los tipos existentes. Por tanto, el programa es ampliable.

Si escribe un mtodo en Java (lo que pronto aprender a hacer) como el siguiente: vod hacerAlgo(Forma forma) { borrar.forma(); // ... dibuj ar.forma();

Este mtodo sirve para cualquier Forma, por lo que es independiente del tipo especfico de objeto que se est dibujando y borrando. Si alguna otra parte del programa utiliza el mtodo hacerAlgo(): Circulo circulo = new Circulo O, Triangulo triangulo = new Triangulo 0; Linea linea = new Linea{); hacerAlgo (circulo); hacerAlgo (triangulo); hacerAlgo (linea);

Las llamadas a hacerAlgo () funcionarn correctamente, independientemente del tipo exacto del objeto.

De hecho, ste es un buen truco. Considere la lnea: hacerAlgo (circulo);

Lo que ocurre aqu es que se est pasando un Circulo en un mtodo que est esperando una Forma. Dado que un Circulo es una Forma, hacerAlgoO puede tratarlo como tal. Es decir, cualquier mensaje que hacerAlgoO pueda enviar a Forma, un circulo puede aceptarlo. Por tanto, actuar as es completamente seguro y lgico.

Llamamos a este proceso de tratar un tipo derivado como si fuera un tipo base upcasting

(generalizacin). La palabra significa en ingls proyeccin hacia arriba" y refleja la forma en que se dibujan habitualmcnte los diagramas de herencia, con el tipo base en la parte superior y las clases derivadas abrindose en abanico hacia abajo, upcasting es, por tanto, efectuar una proyeccin sobre un tipo base, ascendiendo por el diagrama de herencia. Un programa orientado a alguna generalizacin, desvincularse de tener que que se trabaja. Veamos el forma.borrar(); objetos siempre contiene porque es la forma de conocer el tipo exacto con cdigo de hacerAlgoO-

// forma.dibuj ar();

Observe que no se dice, "si eres un Circulo, hacer esto, si eres un Cuadrado, hacer esto, etc.". Con este tipo de cdigo lo que se hace es comprobar todos los tipos posibles de Forma, lo que resulta lioso y se necesitara modificar cada vez que se aadiera una nueva clase de Forma. En este ejemplo, slo se dice: Eres una forma, te puedo borrarO y dibujarQ teniendo en cuenta correctamente los detalles".

Lo que ms impresiona del cdigo del mtodo hacerAlgoO es que. de alguna manera se hace lo correcto. Llamar a dihujarO para Circulo da lugar a que se ejecute un cdigo diferente que cuando se le llama para un Cuadrado o una Linea, pero cuando el mensaje dibujar() se enva a una Forma annima, tiene lugar el comportamiento correcto basndose en el tipo real de la Forma. Esto es impresionante porque, como se ha dicho anteriormente, cuando el compilador Java est compilando el cdigo de hacerAlgoO, no puede saber de forma exacta con qu tipos est tratando. Por ello, habitualmente se espera que llame a la versin de borrar() y dbujar() para la clase base Forma y no a la versin especfica de Crculo. Cuadrado o Linca. Y sigue ocurriendo lo correcto gracias al polimorfismo. El compilador y el sistema de tiempo de ejecucin controlan los detalles; todo lo que hay que saber es qu ocurre y, lo ms importante, cmo disear haciendo uso de ello. Cuando se enva un mensaje a un objeto, el objeto har lo correcto incluso cuando est implicado el proceso de

generalizacin. La jerarqua de raz nica

Uno de los aspectos de la POO que tiene una importancia especial desde la introduccin de C++ es si todas las clases en ltima instancia deberan ser heredadas de una nica clase base. En Java (como en casi todos los dems lenguajes de POO excepto C-H-) la respuesta es afirmativa. Y el nombre de esta clase base es simplemente Object. Resulta que las ventajas de una jerarqua de raz nica son enormes.

Todos los objetos de una jerarqua de raz nica tienen una interfaz en comn, por lo que en ltima instancia son del mismo tipo fundamental. La alternativa (proporcionada por 0+) es no saber que todo es del mismo tipo bsico. Desde el punto de vista de la compatibilidad descendente, esto se ajusta al modelo de C mejor y puede pensarse que es menos restrictivo, pero cuando se quiere hacer programacin orientada a objetos pura debe construirse una jerarqua propia con el fin de proporcionar la misma utilidad que se construye en otros lenguajes de programacin orientada a objetos. Y en cualquier nueva biblioteca de clases que se adquiera, se emplear alguna otra interfaz incompatible. Requiere esfuerzo (y posiblemente herencia mltiple) hacer funcionar la nueva interfaz en un diseo propio. Merece la pena entonces la flexibilidad adicional de C++? Si la necesita (si dispone ya de una gran cantidad de cdigo en C), entonces es bastante valiosa. Si parte de cero, otras alternativas como Java a menudo resultan ms productivas.

Puede garantizarse que todos los objetos de una jerarqua de raz nica tengan una determinada funcionalidad. Es posible realizar determinadas operaciones bsicas sobre todos los objetos del sistema. Pueden crearse todos los objetos y el paso de argumentos se simplifica enormemente.

Una jerarqua de raz nica facilita mucho la implcmentacin de un depurador de memoria, que es una de las mejoras fundamentales de Java sobre C++. Y dado que la informacin sobre el tipo de un objeto est garantizada en todos los objetos, nunca se encontrar con un objeto cuyo tipo no pueda determinarse. Esto es especialmente importante en las operaciones en el nivel del sistema, como por ejemplo el tratamiento de excepciones y para proporcionar un mayor grado de flexibilidad en la programacin. Contenedores

En general, no se sabe cuntos objetos se van a necesitar para resolver un determinado problema o cunto tiempo va a llevar. Tampoco se sabe cmo se van a almacenar dichos objetos. Cmo se puede saber cunto espacio hay que crear si no se conoce dicha informacin hasta el momento de la ejecucin?

La solucin a la mayora de los problemas en el diseo orientado a objetos parece algo poco serio, esta solucin consiste en crear otro tipo de objeto. El nuevo tipo de objeto que resuelve este problema concreto almacena referencias a otros objetos. Por supuesto, se puede hacer lo mismo con una matriz, elemento que est disponible en la mayora de los lenguajes. Pero este nuevo objeto, denominado contenedor (tambin se llama coleccin, pero la biblioteca de Java utiliza dicho trmino con un sentido diferente, por lo que en este libro emplearemos el trmino contenedor'), se amplia por si mismo cuando es necesario acomodar cualquier cosa que se quiera introducir en l. Por tanto, no necesitamos saber cuntos objetos pueden almacenarse en un contenedor. Basta con crear un objeto contenedor y dejarle a l que se ocupe de los detalles.

Afortunadamente, los buenos lenguajes de programacin orientada a objetos incluyen un conjunto de contenedores como parte del paquete. En C++, ese conjunto forma parte de la biblioteca estndar C++ y a menudo se le denomina STL (Standard Templte Library, biblioteca estndar de plantillas). Smalltalk tiene un conjunto muy completo de contenedores, mientras que Java tiene tambin numerosos contenedores en su biblioteca estndar. En algunas bibliotecas, se considera que uno o dos contenedores genricos bastan y sobran para satisfacer todas las necesidades, mientras que en otras (por ejemplo, en Java) la biblioteca tiene diferentes tipos de contenedores para satisfacer necesidades

distintas: varios tipos diferentes de clases List (para almacenar secuencias), Maps (tambin denominados matrices asociativas y que se emplean para asociar objetos con otros objetos). Sets (para almacenar un objeto de cada tipo) y otros componentes como colas, rboles, pilas, etc.

Desde el punto de vista del diseo, lo nico que queremos es disponer de un contenedor que pueda manipularse para resolver nuestro problema. Si un mismo tipo de contenedor satisface todas las necesidades, no existe ninguna razn para disponer de varias clases de contenedor. Sin embargo, existen dos razones por las que s es necesario poder disponer de diferentes contenedores. En primer lugar, cada tipo de contenedor proporciona su propio tipo de interfaz y su propio comportamiento externo. Una pila tiene una interfaz y un comportamiento distintos que una cola, que a su vez es distinto de un conjunto o una lista. Es posible que alguno de estos tipos de contenedor proporcione una solucin ms flexible a nuestro problema que los restantes tipos. En segundo lugar, contenedores diferentes tienen una eficiencia distinta a la hora de realizar determinadas operaciones. Por ejemplo, existen dos tipos bsicos de contenedores de tipo List: ArrayList (lista matricial) y LinkedList (lista enlazada). Ambos son secuencias simples que pueden tener interfaces y comportamientos externos idnticos. Pero ciertas operaciones pueden llevar asociado un coste radicalmente distinto. La operacin de acceder aleatoriamente a los elementos contenidos en un contenedor de tipo ArrayList es una operacin de tiempo constante. Se tarda el mismo tiempo independientemente de cul sea el elemento que se haya seleccionado. Sin embargo, en un contenedor de tipo LinkedList resulta muy caro desplazarse a lo largo de la lista para seleccionar aleatoriamente un elemento, y se tarda ms tiempo en localizar un elemento cuanto ms atrs est situado en la lista. Por otro lado, si se quiere insertar un elemento en mitad de la secuencia, es ms barato hacerlo en un contenedor de tipo LinkedList que en otro de tipo ArrayList. Estas y otras operaciones pueden tener una eficiencia diferente dependiendo de la estructura subyacente de la secuencia. Podemos comenzar construyendo nuestro programa con un contenedor de tipo LinkedList y, a la hora de juzgar las prestaciones, cambiar a otro de tipo ArrayList. Debido a la abstraccin obtenida mediante la interfaz List, podemos cambiar de un tipo de contenedor a otro con un impacto minimo en el cdigo. Tipos parametrizados (genricos)

Antes de Java SE5, los contenedores albergaban objetos del tipo universal de Java: Object. La jerarqua de raz nica indica que todo es de tipo Object. por lo que un contenedor que almacene objetos de tipo Object podr almacenar cualquier cosa. 5 Esto haca que los contenedores fueran fciles de reutilizar.

Los contenedores no permiten almacenar primitivas, pero la caracterstica de atuobxing de Java SE5 hace que esta restriccin tenga pocs importancia. Hablaremos de esto en detalle ms adelante en el libro.
5

Para utilizar uno de estos contenedores, simplemente se aaden a l referencias a objetos y luego se las extrae. Sin embargo, puesto que el contenedor slo permite almacenar objetos de tipo Object, al aadir una referencia a objeto al contenedor, esa referencia se transforma en una referencia a Object perdiendo asi su carcter. Al extraerla, se obtiene una referencia a Object y no una referencia al tipo que se hubiera almacenado. En estas condiciones, cmo podemos transformar esa referencia en algo que tenga el tipo especifico de objeto que hubiramos almacenado en el contenedor?

Lo que se hace es volver a utilizar el mecanismo de transformacin de tipos (casi), pero esta vez no efectuamos una generalizacin. subiendo por la jerarqua de herencia, sino que efectuamos una especializacin, descendiendo desde la jerarqua hasta alcanzar un tipo ms especfico. Este mecanismo de transformacin de tipos se denomina especializacin ( downcas- ting). Con el mecanismo de generalizacin (;upeasting), sabemos por ejemplo que un objeto Circulo es tambin de tipo Forma, por lo que resulta seguro realizar la transformacin de tipos. Sin embargo, no lodo objeto de tipo Object es necesariamente de tipo Circulo o Forma por lo que no resulta tan seguro realizar una especializacin a menos que sepamos concretamente lo que estamos haciendo.

Sin embargo, esta operacin no es del todo peligrosa, porque si efectuamos una conversin de tipos y transformamos el objeto a un tipo incorrecto, obtendremos un error de tipo de ejecucin denominado excepcin (lo que se describe ms adelante). Sin embargo, cuando extraemos referencias a objetos de un contenedor, tenemos que disponer de alguna forma de recordar exactamente lo que son, con el fin de poder realizar la conversin de tipos apropiada.

El mecanismo de especializacin y las comprobaciones en tiempo de ejecucin requieren tiempo adicional para la ejecucin del programa y un mayor esfuerzo por parte del programador. No sera ms lgico crear el contenedor de manera que ste supiera el tipo de los elementos que almacena, eliminando la necesidad de efectuar conversiones de tipos y evitando los errores. asociados? La solucin a este problema es el mecanismo de itpos parametrizados. Un tipo parametrizado es una clase que el compilador puede personalizar automticamente para que funcione con cada tipo concreto. Por ejemplo, con un contenedor parametrizado, el compilador puede personalizar dicho contenedor para que slo acepte y devuelva objetos Forma.

Uno de los cambios principales en Java SE5 es la adicin de tipos parametrizados, que se denominan genricos en Java. El uso de genricos es fcilmente reconocible, ya que emplean corchetes angulares para encerrar alguna especificacin de tipo, por ejemplo, puede crearse un contenedor de tipo ArrayList que almacene objetos de tipo Forma del siguiente modo: ArrayList<Forma> formas = new ArrayList<Forma>();

Tambin se han efectuado modificaciones en muchos de los componentes de las bibliotecas estndar para poder aprovechar el uso de genricos. Como tendremos oportunidad de ver, los genricos tienen una gran importancia en buena parte del cdigo utilizado en este libro. Creacin y vida de los objetos

Una de las cuestiones crticas a la hora de trabajar con los objetos es la forma en que stos se crean y se destruyen. Cada objeto consigue una serie de recursos, especialmente memoria, para poder simplemente existir. Cuando un objeto deja de ser necesario, es preciso eliminarlo, para que se liberen estos recursos y puedan emplearse en alguna otra cosa. En los casos ms simples de programacin, el problema de borrar los objetos no resulta demasiado complicado. Creamos el objeto, lo usamos mientras que es necesario y despus lo destruimos. Sin embargo, no es difcil encontrarse situaciones bastante ms complejas que sta.

Suponga por ejemplo que estamos diseando un sistema para gestionar el trfico areo de un aeropuerto (el mismo modelo servira para gestionar piezas en un almacn o para un sistema de alquiler de videos o para una tienda de venta de mascotas). A primera vista, el problema parece muy simple: creamos un contenedor para almacenar las aeronaves y luego creamos una nueva aeronave y la insertamos en el contenedor por cada una de las aeronaves que entren en la zona de control del trfico areo. De cara al borrado, simplemente basta con eliminar el objeto aeronave apropiado en el momento en que el avin abandone la zona.

Pero es posible que tengamos algn otro sistema en el que queden registrados los datos acerca de los aviones; quiz se trate de datos que no requieran una atencin tan inmediata como la de la funcin principal de control del trfico areo. Puede que se trate de un registro de los planes de vuelo de todos los pequeos aeroplanos que salgan del aeropuerto. Entonces, podramos definir un segundo contenedor para esos aeroplanos y, cada vez que se creara un objeto aeronave, se introducira tambin en este segundo contenedor si se trata de un pequeo aeroplano. Entonces, algn proceso de segundo plano podra realizar operaciones sobre los objetos almacenados en este segundo contenedor en los momentos de inactividad.

Ahora el problema ya es ms complicado: cmo podemos saber cundo hay que destruir los objetos? An cuando nosotros hayamos terminado de procesar un objeto, puede que alguna otra parte del sistema no lo haya hecho. Este mismo problema puede surgir en muchas otras situaciones, y puede llegar a resultar enormemente complejo de resolver en aquellos sistemas de programacin (como C+ +) en los que es preciso borrar explcitamente un objeto cuando se ha terminado de utilizar.

Dnde se almacenan los datos correspondientes a un objeto y cmo se puede controlar el tiempo de vida del mismo? En C++. se adopta el enfoque de que el control de la eficiencia es el tema ms importante, por lo que todas las decisiones quedan en manos del programador. Para conseguir la mxima velocidad de ejecucin, las caractersticas de almacenamiento y del tiempo de vida del objeto pueden determinarse mientras se est escribiendo el programa, colocando los objetos en la pila (a estos objetos se los denomina en ocasiones variables automticas o de mbito) o en el rea de almacenamiento esttico. Esto hace que lo ms prioritario sea la velocidad de asignacin y liberacin del almacenamiento, y este control puede resultar muy til en muchas situaciones. Sin embargo, perdemos flexibilidad porque es preciso conocer la cantidad, el tiempo de vida y el tipo exacto de los objetos a la hora de escribir el programa. Si estamos tratando de resolver un problema ms general. como por ejemplo, un programa de diseo asistido por computadora, un sistema de gestin de almacn o un sistema de control de trfico areo, esta solucin es demasiado restrictiva.

La segunda posibilidad consiste en crear los objetos dinmicamente en un rea de memoria denominada cmulo. Con este enfoque, no sabemos hasta el momento de la ejecucin cuntos objetos van a ser necesarios, cul va a ser su tiempo de vida ni cul es su tipo exacto. Todas estas caractersticas se determinan en el momento en que se ejecuta el programa. Si hace falla un nuevo

objeto, simplemente se crea en el cmulo de memoria, en el preciso instante en que sea necesario. Puesto que el almacenamiento se gestiona dinmicamente en tiempo de ejecucin, la cantidad de tiempo requerida para asignar el almacenamiento en el cmulo de memoria puede ser bastante mayor que el tiempo necesario para crear un cierto espacio en la pila. La creacin de espacio de almacenamiento en la pila requiere normalmente una nica instruccin de ensamblador, para desplazar hacia abajo el puntero de la pila y otra instruccin para volver a desplazarlo hacia arriba. El tiempo necesario para crear un espacio de almacenamiento en el cmulo de memoria depende del diseo del mecanismo de almacenamiento.

La solucin dinmica se basa en la suposicin, generalmente bastante lgica, de que los objetos suelen ser complicados, por

lo que el tiempo adicional requerido para localizar el espacio de almacenamiento y luego liberarlo no tendr demasiado impacto sobre el proceso de creacin del objeto. Adems, el mayor grado de flexibilidad que se obtiene resulta esencial para resolver los problemas de programacin de carcter general.

Java utiliza exclusivamente un mecanismo dinmico de asignacin de memoria 6. Cada vez que se quiere crear un objeto, se utiliza el operador ne>v para construir una instancia dinmica del objeto.

Sin embargo, existe otro problema, referido al tiempo de vida de un objeto. En aquellos lenguajes que permiten crear objetos en la pila, el compilador determina cul es la duracin del objeto y puede desunirlo automticamente. Sin embargo, si creamos el objeto en el cmulo de memoria, el
6

Los tipos primitivos, de los que hablaremos en breve rcpresenlun un caso especial.

compilador no sabe cul es su tiempo de vida. En un lenguaje como C++, es preciso determinar mediante programa cundo debe destruirse el objeto, lo que puede provocar prdidas de memoria si no se realiza esta tarea correctamente (y este problema resulta bastante comn en los programas C++). Java proporciona una caracterstica denominada depurador de memoria, que descubre automticamente cundo un determinado objeto ya no est en uso. en cuyo caso lo destruye. Un depurador de memoria resulta mucho ms cmodo que cualquier otra solucin alternativa, porque reduce el nmero de problemas que el programador debe controlar, y reduce tambin la cantidad de cdigo que hay que escribir. Adems, lo que resulta ms importante, el depurador de memoria proporciona un nivel mucho mayor de proteccin contra el insidioso problema de las fugas de memoria, que ha hecho que muchos proyectos en C++ fracasaran.

En Java, el depurador de memoria est diseado para encargarse del problema de liberacin de la memoria (aunque esto no incluye otros aspectos relativos al borrado de un objeto). El depurador de memoria sabe* cundo ya no se est usando un objeto, en cuyo caso libera automticamente la memoria correspondiente a ese objeto. Esta caracterstica, combinada con el hecho de que todos los objetos heredan de la clase raz Object. y con el hecho de que slo pueden crearse objetos de una manera (en el cmulo de memoria), hace que el proceso de programacin en Java sea mucho ms simple que en C++. hay muchas menos decisiones que tomar y muchos menos problemas que resolver. Tratamiento de excepciones: manejo de errores

Desde la aparicin de los lenguajes de programacin, el tratamiento de los errores ha constituido un problema peculiarmente difcil. Debido a que resulta muy complicado disear un buen esquema de tratamiento de errores, muchos lenguajes simplemente ignoran este problema, dejando que lo resuelvan los diseadores de bibliotecas, que al final terminan desarrollando soluciones parciales que funcionan en muchas situaciones pero cuyas medidas pueden ser obviadas fcilmente: generalmente, basta con ignorarlas. Uno de los problemas principales de la mayora de los esquemas de tratamiento de errores es que dependen de que el programador tenga cuidado a la hora de seguir un convenio preestablecido que no resulta obligatorio dentro del lenguaje. Si el programador no tiene cuidado (lo cual suele suceder cuando hay prisa por terminar un proyecto), puede olvidarse fcilmente de estos esquemas.

Los mecanismos de tratamiento de excepciones integran la gestin de errores directamente dentro del lenguaje de programacin. en ocasiones, dentro incluso del sistema operativo. Una excepcin no es ms que un objeto generado en el lugar donde se ha producido el error y que puede ser capturado mediante una rutina apropiada de tratamiento de excepciones diseada para gestionar dicho tipo particular de error. Es como si el tratamiento de excepciones ftiera una rula de ejecucin paralela y diferente, que se loma cuando algo va mal. Y. como se utiliza una rata de ejecucin independiente, sta no tiene porqu interferir con el cdigo que se ejecuta normalmente. Esto hace que el cdigo sea ms simple de escribir, porque no es necesario comprobar constantemente la existencia de errores. Adems, las excepciones generadas se diferencian de los tpicos valores de error devueltos por los mtodos o por los indicadores activados por los mtodos para avisar que se ha producido una condicin de error: tanto los valores como los indicadores de error podran ser ignorados por el programador. Las excepciones no pueden ignorarse, por lo que se garantiza que en algn momento sern tratadas. Finalmente, las excepciones proporcionan un mecanismo para recuperarse de manera fiable de cualquier situacin errnea. En lugar de limitarse a salir del programa, a menudo podemos corregir las cosas y restaurar la ejecucin, lo que da como resultado programas mucho ms robustos.

El tratamiento de excepciones de Java resulta muy sobresaliente entre los lenguajes de programacin, porque en Java el tratamiento de excepciones estaba previsto desde el principio y estamos obligados a utilizarlo. Este esquema de tratamiento de excepciones es el nico mecanismo aceptable en Java para informar de la existencia de errores. Si no se escribe el cdigo de manera que trate adecuadamente las excepciones se obtiene un error en tiempo de compilacin. Esta garanta de coherencia puede hacer que, en ocasiones, el tratamiento de errores sea mucho ms sencillo.

Merece la pena resaltar que el tratamiento de excepciones no es una caracterstica orientada a objetos, aunque en los lenguajes de programacin orientada a objetos las excepciones se representan normalmente mediante un objeto. Los mecanismos de tratamiento de excepciones ya existan antes de que hicieran su aparicin los lenguajes orientados a objetos. Programacin concurrente

Un concepto fundamental en el campo de la programacin es la idea de poder gestionar ms de una

tarea al mismo tiempo. Muchos problemas de programacin requieren que el programa detenga la tarea que estuviera realizando, resuelva algn otro problema y luego vuelva al proceso principal. A lo largo del tiempo, se ha tratado de aplicar diversas soluciones a este problema. Inicialmente, los programadores que tenan un adecuado conocimiento de bajo nivel de la mquina sobre la que estaban programando escriban rutinas de servicio de interrupcin, y la suspensin del proceso principal se llevaba a cabo mediante una intemipcin hardware. Aunque este esquema funcionaba bien, resultaba complicado y no era portable, por lo que traducir un programa a un nuevo tipo de mquina resultaba bastante lento y muy caro.

En ocasiones, las interrupciones son necesarias para gestionar las tareas con requisitos crticos de tiempo, pero hay una amplia clase de problemas en la que tan slo nos interesa dividir el problema en una serie de fragmentos que se ejecuten por separado (tareas), de modo que el programa completo pueda tener un mejor tiempo de respuesta. En un programa, estos fragmentos que se ejecutan por separado, se denominan hebras y el conjunto general se llama concurrencia. Un ejemplo bastante comn de concurrencia es la interfaz de usuario. Utilizando distintas tareas, el usuario apretando un botn puede obtener una respuesta rpida, en lugar de tener que esperar a que el programa finalice con la tarea que est actualmente realizando.

Normalmente, las tareas son slo una forma de asignar el tiempo disponible en un nico procesador. Pero si el sistema operativo soporta mltiples procesadores, puede asignarse cada tarea a un procesador distinto, en cuyo caso las tareas pueden ejecutarse realmente en paralelo. Una de las ventajas de incluir los mecanismos de concurrencia en el nivel de lenguaje es que el programador no tiene que preocuparse de si hay varios procesadores o slo uno; el programa se divide desde el punto de vista lgico en una serie de tareas, y si la mquina dispone de ms de un procesador, el programa se ejecutar ms rpido, sin necesidad de efectuar ningn ajuste especial.

Todo esto hace que la concurrencia parezca algo bastante sencillo, pero existe un problema: los recursos compartidos. Si se estn ejecutando varias tareas que esperan poder acceder al mismo recurso, tendremos un problema de contienda entre las tareas. Por ejemplo, no puede haber dos procesadores enviando informacin a una misma impresora. Para resolver el problema, los recursos que puedan compartirse, como por ejemplo una impresora, deben bloquearse mientras estn siendo utilizados por una tarea. De manera que la forma de funcionar es la siguiente: una tarea bloquea un

recurso, completa el trabajo que tuviera asignado y luego elimina el bloqueo para que alguna otra tarea pueda emplear el recurso.

Los mecanismos de concurrencia en Java estn integrados dentro de lenguaje y Java SE5 ha mejorado significativamente el soporte de biblioteca para los mecanismos de concurrencia. Java e Internet

Si Java no es, en definitiva, ms que otro lenguaje informtico de programacin, podramos preguntamos por qu es tan importante y por qu se dice de l que representa una autntica revolucin dentro del campo de la programacin. La respuesta no resulta obvia para aqullos que provengan del campo de la programacin tradicional. Aunque Java resulta muy til para resolver problemas de programacin en entornos autnomos, su importancia se debe a que permite resolver los problemas de programacin que surgen en la World Wide Web. Qu es la Web?

Al principio, la Web puede parecer algo misterioso, con todas esas palabras extraas como surfear, presencia web y pginas de inicio. Resulta til, para entender los conceptos, dar un paso atrs y tratar de comprender lo que la Web es realmente, pero para ello es necesario comprender primero lo que son los sistemas cliente/servidor, que constituyen otro campo de la informtica lleno de conceptos bastante confusos.

Informtica cliente/servidor

La idea principal en la que se basan los sistemas cliente/servidor es que podemos disponer de un repositorio centralizado de informacin (por ejemplo, algn tipo de datos dentro de una base de datos) que queramos distribuir bajo demanda a una serie de personas o de computadoras. Uno de los conceptos clave de las arquitecturas cliente/servidor es que el repositorio de informacin est centralizado, por lo que puede ser modificado sin que esas modificaciones se propaguen hasta los consumidores de la informacin. El repositorio de informacin, el software que distribuye la informacin y la mquina o mquinas donde esa informacin y ese software residen se denominan, en conjunto, servidor. El software que reside en las mquinas consumidoras, que se comunica con el servidor, que extrae la informacin, que la procesa y que luego la muestra en la propia mquina consumidora se denomina cliente.

El concepto bsico de informtica cliente/servidor no es. por tanto, demasiado complicado. Los problemas surgen porque disponemos de un nico servidor tratando de dar servicio a mltiples clientes al mismo tiempo. Generalmente, se utiliza algn tipo de sistema de gestin de bases de datos de modo que el diseador equilibra la disposicin de los datos entre distintas tablas, con el fin de optimizar el uso de los datos. Adems, estos sistemas permiten a menudo que los clientes inserten nueva informacin dentro de un servidor. Esto quiere decir que es preciso garantizar que los nuevos datos de un cliente no sobreescriban los nuevos datos de otro cliente, al igual que hay que garantizar que no se pierdan datos en el proceso de aadirlos a la base de datos (este tipo de mecanismos se denomina procesamiento de transacciones). A medida que se realizan modificaciones en el software de clienie, es necesario disear el software, depurarlo e instalarlo en las mquinas cliente. lo que resulta ser ms complicado y ms caro de lo que en un principio cabria esperar. Resulta especialmente problemtico soportar mltiples tipos de computadoras y de sistemas operativos. Finalmente, es necesario tener en cuenta tambin la cuestin crucial del rendimiento: puede que tengamos cientos de clientes enviando cientos de solicitudes al servidor en un momento dado, por lo que cualquier pequeo retardo puede llegar a ser verdaderamente critico. Para minimizar la latencia. los programadores hacen un gran esfuerzo para tratar de descargar las tareas de procesamiento que er ocasiones se descargan en la mquina cliente, pero en otras ocasiones se descargan en otras mquinas situadas junto al servidor, utilizando un tipo especial de software denominado middleware, (el middleware se utiliza tambin para mejorar la facilidad de mantenimiento del sistema).

Esa idea tan simple de distribuir la informacin tiene tantos niveles de complejidad que el problema global puede parecer enigmticamente insoluble. A pesar de lo cual, se trata de un problema crucial: la informtica cliente/servidor representa aproximadamente la mitad de las actividades de programacin en la actualidad. Este tipo de arquitectura es responsable de todo tipo de tareas, desde la introduccin de pedidos y la realizacin de transacciones con tarjetas de crdito hasta la distribucin de cualquier tipo de datos, como por ejemplo cotizaciones burstiles, datos cientficos, informacin de organismos gubernamentales. En el pasado, lo que hemos hecho es desarrollar soluciones individuales para problemas individuales, inventando una nueva solucin en cada ocasin. Esas soluciones eran difciles de disear y de utilizar, y el usuario se vea obligado a aprender una nueva interfaz en cada caso. De este modo, se lleg a un punto en que era necesario resolver el problema global de la informtica cliente/servidor de una vez y para siempre. La Web como un gigantesco servidor

La Web es. en la prctica, un sistema gigantesco de tipo cliente/servidor. En realidad, es todava ms complejo, ya que lo que tenemos es un conjunto de servidores y clientes que coexisten en una misma red de manera simultnea. El usuario no necesita ser consciente de ello, por lo que lo nico que hace es conectarse con un servidor en cada momento c interactuar con l (an cuando para llegar a ese servidor haya sido necesario ir saltando de servidor en servidor por todo el mundo hasta dar con el correcto).

Inicialmente, se trataba de un proceso muy simple de carcter unidireccional: el usuario enviaba una solicitud al servidor y este le devolva un archivo, que el software explorador de la mquina (es dccir, el cliente) se encargaba de interpretar, efectuando todas las tareas de formateo en la propia mquina local. Pero al cabo de muy poco tiempo, los propietarios de servidores comenzaron a querer hacer cosas ms complejas que simplemente suministrar pginas desde el servidor. Queran disponer de una capacidad completa cliente/servidor, de forma que el cliente pudiera, por ejemplo enviar informacin al servidor. realizar bsquedas en una base de datos instalada en el servidor, aadir nueva informacin al servidor o realizar un pedido (lo que requiere medidas especiales de seguridad). Estos son los cambios que hemos vivido en los ltimos aos en el desarrollo de la Web.

Los exploradores web representaron un gran avance: permitieron implementar el concepto de que un

mismo fragmento de informacin pudiera visualizarse en cualquier tipo de computadora sin necesidad de efectuar ninguna modificacin. Sin embargo, los primeros exploradores eran bastante primitivos y se colapsaban rpidamente debido a las demandas que se les haca. No resultaban peculiarmente interactivos y tendan a sobrecargar al servidor tanto como a la propia red Internet, porque cada vez que haca falta hacer algo que requera programacin, era necesario devolver la informacin al servidor para que ste la procesara. De esta forma, poda tardarse varios segundos o incluso minutos en averiguar simplemente que habamos tecleado incorrectamente algo dentro de la solicitud. Como el explorador era simplemente un mecanismo de visua- lizacin no poda realizar ni siquiera la ms simple de las tareas (por otro lado, resultaba bastante seguro ya que no poda ejecutar en la mquina local ningn programa que pudiera contener errores o virus).

Para resolver este problema, se han adoptado diferentes enfoques. Para empezar se han mejorado los estndares grficos para poder disponer de mejores animaciones y vdeos dentro de los exploradores. El resto del problema slo puede resolverse incorporando la capacidad de ejecutar programas en el extremo cliente, bajo control del explorador. Esto se denomina programacin del lado del cliente. Programacin del lado del cliente

El diseo inicial de la Web. basado en una arquitectura servidor/explorador, permita disponer de contenido interactivo, pero esa mteractividad era completamente proporcionada por el servidor. El servidor generaba pginas estticas para el explorador cliente, que simplemente se encargaba de interpretarlas y mostrarlas. El lenguaje bsico HTML (HyperText Karkup Language) contiene una serie de mecanismos simples para la introduccin de datos; recuadros de introduccin de texto, casillas de verificacin, botones de opcin, listas normales y listas desplcgables, as como un botn que slo podia programarse para borrar los datos del formulario o enviarlos al servidor. Ese proceso de envo se llevaba a cabo a travs de la interfaz COI (Common Gateway Interface) incluida en todos los serv idores web. El texto incorporado en el envo le dice a la interfaz CGI lo que tiene que hacer. La accin ms comn, en este caso, consiste en ejecutar un programa ubicado en un servidor en un directorio normalmente llamado egi-bin (si observa la barra de direcciones situada en la parte superior del explorador cuando pulse un botn en una pgina web, en ocasiones podr ver las palabras egi-bin* como parte de la direccin). Estos programas del lado del servidor pueden escribirse en casi cualquier lenguaje. Perl es uno de los lenguajes ms utilizados para este Upo de tareas, porque est diseado especficamente para la manipulacin de textos y es un lenguaje interpretado, por lo que se puede instalar en cualquier servidor independientemente de cul sea su procesador o su sistema operativo. Sin embargo, otro lenguaje. Python (www.Python.org) se est abriendo camino rpidamente, debido a su mayor potencia y su mayor simplicidad.

Hay muchos sitios web potentes en la actualidad diseados estrictamente con CGI, y lo cierto es que con CGI se puede hacer prcticamente de todo. Sin embargo, esos sitios web basados en programas CGI pueden llegar a ser rpidamente bastante complicados de mantener, y adems pueden aparecer problemas en lo que se refiere al tiempo de respuesta. El tiempo de respuesta de un programa CGI depende de cuntos datos haya que enviar, de la carga del servidor y de la red Internet (adems. el propio arranque de un programa CGI liende a ser bastante lento). Los primeros diseadores de la Web no previeron la rapidez con que el ancho de banda disponible iba a agotarse debido a los tipos de aplicaciones que la gente llegara a desarrollar. Por ejemplo, es casi imposible disear de manera coherente una aplicacin con grficos dinmicos, porque es necesario crear un archivo GIF (Graphics Interchange Formal) y enviarlo del servidor al cliente para cada versin de grfico. Adems, casi todos los usuarios hemos experimentado lo engorroso del proceso de validacin de los datos dentro de un formulario enviado a travs de la Web. El proceso es el siguiente: pulsamos el botn de envo de la pgina; se envan los datos al servidor, el servidor arranca un programa CGI que descubre un error, formatea una pgina H TML en la que nos informa del error y devuelve la pgina al cliente; a continuacin, es necesario que el usuario retroceda una pgina y vuelva a intentarlo. Este enfoque no slo resulta lento sino tambin poco elegante.

La solucin consiste en usai un mecanismo de programacin del lado del cliente. La mayora de las computadoras de sobremesa que incluyen un explorador web son mquinas bastante potentes, capaces de realizar tareas muy complejas; con el enfoque original basado en HTML esttico, esas potentes mquinas simplemente se limitan a esperar sin hacer nada, hasta que el servidor se digna a enviarles la siguiente pgina. La programacin del lado del cliente permite asignar al explorador web todo el trabajo que pueda llevar a cabo, con lo que el resultado para los usuarios es una experiencia mucha ms rpida y ms interactiva a la hora de acceder a los sitios web.

F.l problema con las explicaciones acerca de la programacin del lado del cliente es que no se diferencian mucho de las explicaciones relativas a la programacin en general. Los parmetros son prcticamente idnticos, aunque la plataforma sea distinta: un explorador web es una especie de sistema operativo limitado. En ltimo trmino, sigue siendo necesario disear programas, por lo que los problemas y soluciones que nos encontramos dentro del campo de la programacin del lado del cliente son bastante tradicionales. En el resto de esta seccin, vamos a repasar algunos de los principales problemas y tcnicas que suelen encontrarse en el campo de la programacin del lado del cliente.

Plug-ins

Uno de los avances ms significativos en la programacin del lado del cliente es el desarrollo de lo que se denomina plug- in. Se trata de un mecanismo mediante el que un programador puede aadir algn nuevo tipo de funcionalidad a un explorador descargando un fragmento de cdigo que se inserta en el lugar apropiado deniro del explorador. Esc fragmento de cdigo le dice al explorador: A partir de ahora puedes realizar este nuevo tipo de actividad" (slo es necesario descargar el plug-in una vez). Podemos aadir nuevas formas de comportamiento, potentes y rpidas, a los exploradores mediante plug-ins, pero la escritura de un plug-in no resulta nada trivial, y por eso mismo no es conveniente acometer ese tipo de tarea como parte del proceso de construccin de un sitio web. El valor de un plug-in para la programacin del lado del clienie es que permite a los programadores avanzados desarrollar extensiones y aadrselas a un explorador sin necesidad de pedir permiso al fabricante del explorador. De esta forma, los plug-ins proporcionan una especie de puerta trasera que permite la creacin de nuevos lenguajes de programacin del lado del cliente (aunque no todos los lenguajes se implementan como plug-ins). Lenguajes de script

Los plug-ins dieron como resultado el desarrollo de lenguajes de script para los exploradores. Con un lenguaje de script, el cdigo fuente del programa del lado del cliente se integra directamente dentro de la pgina HTML, y el plug-in que se encarga de interpretar ese lenguaje se activa de manera automtica en el momento de visualizar la pgina HTML. Los lenguajes de script suelen ser razonablemente fciles de comprender, y como estn formados simplemente por texto que se incluye dentro de la propia pgina HTML, se cargan muy rpidamente como parte del acceso al servidor mediante el que se obtiene la pgina. La desventaja es que el cdigo queda expuesto, ya que cualquiera puede verlo (y copiarlo). Generalmente, sin embargo, los programadores no llevan a cabo tareas extremadamente sofisticadas con los lenguajes de script, as que este problema no resulta particularmente grave.

Uno de los lenguajes de script que los exploradores web suelen soportar sin necesidad de un plug-in es JavaScript (el lenguaje JavaScript slo se asemeja de forma bastante vaga a Java, por lo que hace falta un esfuerzo de aprendizaje adicional para llegar a dominarlo; recibi el nombre de JavaScript simplemente para aprovechar el impulso inicial de marketing de Java), lamentablemente, cada

explorador web implcmentaba originalmente JavaScript de forma distinta a los restantes exploradores web, e incluso en ocasiones, de forma diferente a otras versiones del mismo explorador. La estandarizacin de JavaScript mediante el diseo del lenguaje estndar ECMAScript ha resuelto parcialmente este problema, pero tuvo que transcurrir bastante tiempo hasta que los distintos exploradores adoptaron el estndar (el problema se complic porque Microsoft trataba de conseguir sus propios objetivos presionando en favor de su lenguaje VBScript, que tambin se asemejaba vagamente a JavaScript). En general, es necesario llevar a cabo la programacin de las pginas utilizando una especie de mnimo comn denominador de JavaScript, si lo que queremos es que esas pginas puedan visualizarse en todos los tipos de exploradores. Por su parte, la solucin de errores y la depuracin en JavaScript son un autntico lo. Como prueba de lo difcil que resulta disear un problema complejo con JavaScript, slo muy recientemente alguien se ha atrevido a crear una aplicacin compleja basada en l (Google. con GMail), y ese desarrollo requiri una dedicacin y una experiencia realmente notables.

Lo que todo esto nos sugiere es que los lenguajes de script que se emplean en los exploradores web estn diseados, realmente. para resolver tipos especficos de problemas, principalmente el de la creacin de interfaces grficas de usuario (GUI) ms ricas e interactivas. Sin embargo, un lenguaje de script puede resolver quiz un 80 por ciento de los problemas que podemos encontrar en la programacin del lado del cliente. Es posible que los problemas que el lector quiera resolver estn incluidos dentro de ese 80 por ciento. Si esto es asi, y teniendo en cuenta que los lenguajes de script permiten realizar los desarrollos de forma ms fcil y rpida, probablemente seria conveniente ver si se puede resolver un tipo concreto de problema empleando un lenguaje de script, antes de considerar otras soluciones ms complejas, como la programacin en Java. Java

Si un lenguaje de script puede resolver el 80 por ciento de los problemas de la programacin del lado del cliente, qu pasa con el otro 20 por ciento, con los "problemas realmente difciles? Java representa una solucin bastante popular para este tipo de problemas. No slo se trata de un potente lenguaje de programacin diseado para ser seguro, interplataforma c internacional, sino que continuamente est siendo ampliado para proporcionar nuevas caractersticas del lenguaje y nuevas bibliotecas que permiten gestionar de manera elegante una serie de problemas que resultan bastante difciles de tratar en los lenguajes de programacin tradicionales, como por ejemplo la concurrencia, el acceso a bases de datos, la programacin en red y la informtica distribuida. Java permite resolver los problemas de programacin del lado del cliente utilizando applets y Java Web Start.

Un applet es un mini-programa que slo puede ejecutarse sobre un explorador web. El applet se descarga automticamente como parte de la pgina web (de la misma forma que se descarga automticamente, por ejemplo, un grfico). Cuando se activa el applet. ejecuta un programa. Este mecanismo de ejecucin automtica forma parte de la belleza de esta solucin: nos proporciona una forma de distribuir automticamente el software de cliente desde el servidor en el mismo momento en que c! usuario necesita ese software de cliente, y no antes. El usuario obtiene la ltima versin del software de cliente, libre de errores y sin necesidad de realizar complejas reinstalaciones. Debido a la forma en que se ha diseado Java, el programador slo tiene que crear un programa simple y ese programa funcionar automticamente en todas las computadoras que dispongan de exploradores que incluyan un intrprete integrado de Java (lo que incluye la inmensa mayora de mquinas). Puesto que Java es un lenguaje de programacin completo podemos llevar a cabo la mayor cantidad de trabajo posible en el cliente, tanto antes como despus de enviar solicitudes al servidor. Por ejemplo, no es necesario enviar una solicitud a travs de Internet simplemente para descubrir que hemos escrito mal una fecha o algn otro parmetro: asimismo, la computadora cliente puede encargarse de manera rpida de la tarea de dibujar una serie de datos, en lugar de esperar a que el servidor genere el grfico y devuelva una imagen al explorador. De este modo, no slo aumentan de forma inmediata la velocidad y la capacidad de respuesta, sino que disminuyen tambin la carga de trabajo de los servidores y el trfico de red, evitando asi que todo Internet se ralentice. Alternativas

Para ser honestos, los applets Java no han llegado a cumplir con las expectativas iniciales. Despus del lanzamiento de Java, pareca que los applets era lo que ms entusiasmaba a todo el mundo, porque iban a permitir finalmente realizar tareas serias de programacin del lado del cliente, iban a mejorar la capacidad de respuesta de las aplicaciones basadas en Internet e iban a reducir el ancho de banda necesario. Las posibilidades que todo el mundo tena en mente eran inmensas.

Sin embargo, hoy dia nos podemos encontrar con unos applets realmente interesantes en la Web, pero la esperada migracin masiva hacia los applets no lleg nunca a producirse. El principal problema era que la descarga de 10 MB necesaria para instalar el entorno de ejecucin JRE (Java Runtime Environment) era demasiado para el usuario medio. El hecho de que Microsoft decidiera no incluir el entorno JRE dentro de Internet Explorer puede ser lo que acab por determinar su aciago destino. Pero, sea como sea, lo cieno es que los applets Java no han llegado nunca a ser utilizados de forma masiva.

A pesar de todo, los applets y las aplicaciones Java Web Start siguen siendo adecuadas en algunas situaciones. En todos aquellos casos en que tengamos control sobre las mquinas de usuario, por ejemplo en una gran empresa, resulta razonable distribuir y actualizar las aplicaciones cliente utilizando este tipo de tecnologas, que nos pueden ahorrar una cantidad considerable de tiempo, esfuerzo y dinero, especialmente cuando es necesario realizar actualizaciones frecuentes.

En el Captulo 22, Interfaces grficas de usuario, analizaremos una buena tecnologa bastante prometedora, Flex de Macromedia, que permite crear equivalentes a los applets basados en Flash. Como el reproductor Flash Player est disponible en ms del 98 por ciento de todos los exploradores web (incluyendo Windows, Linux y Mac), puede considerarse como un estndar de facto. La instalacin o actualizacin de Flash Player es asimismo rpida y fcil. El lenguaje ActionScript est basado en ECMAScript, por lo que resulta razonablemente familiar, pero Flex permite realizar las tareas de programacin sin preocuparse acerca de las especifidades de los exploradores, por lo que resulta bastante ms atractivo que JavaScript. Para la programacin del lado del cliente se trata de una alternativa que merece la pena considerar. .NET y C#

Durante un tiempo, el competidor principal de los applets de Java era ActiveX de Microsoft, aunque esta tecnologa requera que en el cliente se estuviera ejecutando el sistema operativo Windows. Desde entonces, Microsoft ha desarrollado un competidor de Java: la plataforma .NET y el lenguaje de programacin C#. La plataforma .NET es. aproximadamente, equivalente a la mquina virtual Java (JVM, Java Virtual Machine; es la plataforma software en la que se ejecutan los programas Java) y a las bibliotecas Java, mientras que C# tiene similitudes bastante evidentes con Java. Se trata, ciertamente, del mejor intento que Microsoft ha llevado a cabo en el rea de los lenguajes de programacin. Por supuesto. Microsoft parta con la considerable ventaja de conocer qu cosas haban funcionado de manera adecuada y qu cosas no funcionaban tan bien en Java, por lo que aprovech esos conocimientos. Desde su concepcin, es la primera vez que Java se ha encontrado con un verdadero competidor. Como resultado, los diseadores de Java en Sun han analizado intensivamente CU y las razones por las que un programador podra sentirse tentado a adoptar ese lenguaje, y han respondido introduciendo significativas mejoras en Java, que han resultado en el lanzamiento de Java SE5.

Actualmente, la debilidad principal y el problema ms importante en relacin con .NET es si Microsoft permitir portarlo completamente a otras plataformas. Ellos afirman que no hay ningn problema para esto, y el proyecto Mono (vnnvgo- mono.com) dispone de una implementacin parcial de .NET sobre Linux, pero hasta que la implementacin sea completa y Microsoft decida no recortar ninguna pane de la misma, sigue siendo una apuesta arriesgada adoptar .NET como solucin interplataforma. Redes Internet e intranet

La Web es la solucin ms general para el problema de las arquitecturas cliente/servidor, por lo que tiene bastante sentido utilizar esta misma tecnologa para resolver un cierto subconjunto de ese problema: el problema clsico de las arquitecturas clientc/servidor internas a una empresa. Con las tcnicas tradicionales cliente/servidor, nos encontramos con el problema de la existencia de mltiples tipos de computadoras cliente, as como con la dificultad de instalar nuevo software de cliente: los exploradores web y la programacin del lado del cliente permiten resolver fcilmente ambos problemas. Cuando se utiliza tecnologa web para una red de informacin restringida a una empresa concreta, la arquitectura resultante se denomina intranet. Las intranets proporcionan un grado de seguridad mucho mayor que Internet, ya que podemos controlar fsicamente el acceso a los equipos de la empresa. En trminos de formacin, una vez que los usuarios comprenden el concepto general de explorador les resulta mucho ms fcil asumir las diferencias de aspecto entre las distintas pginas y applets, por lo que la curva de aprendizaje para los nuevos tipos de sistemas se reduce.

El problema de seguridad nos permite analizar una de las divisiones que parecen estarse formando de manera automtica en el mundo de la programacin del lado del cliente. Si nuestro programa se est ejecutando en Internet no sabemos en que plataforma se ejecutar y adems es necesario poner un cuidado adicional en no diseminar cdigo que contenga errores. En estos casos, es necesario disponer de un lenguaje interplataforma y seguro, como por ejemplo, un lenguaje de script o Java.

Si nuestra aplicacin se ejecuta en una intranet, es posible que el conjunto de restricciones sea distinto. No resulta extrao que todas las mquinas sean plataformas Intel/Windows. En una intranet, nosotros somos responsables de la calidad de nuestro propio cdigo y podemos corregir los errores en el momento en que se descubran. Adems, puede que ya dispongamos de una gran cantidad de cdigo

heredado que haya estado siendo utilizado en alguna arquitectura cliente/servidor ms tradicional, en la que es necesario instalar fsicamente los programas cliente cada vez que se lleva a cabo una actualizacin. El tiempo que se pierde a la hora de instalar actualizaciones es, precisamente, la principal razn para comenzar a utilizar exploradores. porque las actualizaciones son invisibles y automticas (Java Web Start tambin constituye una solucin a este problema). Si trabajamos en una intranet de este tipo, la solucin ms lgica consiste en seguir la ruta ms corta que nos permita utilizar la base de cdigo existente, en lugar de volver a escribir todos los programa en un nuevo lenguaje.

Al enfrentarse con este amplio conjunto de soluciones para los problemas de la programacin del lado del cliente, el mejor plan de ataque consiste en realizar un anlisis de coste-beneficio. Considere las restricciones que afectan a su problema y cul sera la ruta ms corta para encontrar una solucin. Puesto que la programacin del lado del cliente sigue siendo una programacin en sentido tradicional, siempre resulta conveniente adoptar el enfoque de desarrollo ms rpido en cada situacin concreta. Esta es la mejor manera de prepararse para los problemas que inevitablemente enconnaremos a la hora de desarrollar los programas. Programacin del lado del servidor

En nuestro anlisis, hemos ignorado hasta ahora la cuestin de la programacin del lado del servidor, que es probablemente donde Java ha tenido su xito ms rotundo. Qu sucede cuando enviamos una solicitud a un servidor? La mayor parte de las veces, la solicitud dice simplemente Envame este archivo. A continuacin, el explorador interpreta el archivo de la forma apropiada, como pgina HTML, como imagen, como un applet de Java, como programa en lenguaje de script, etc.

Las solicitudes ms complicadas dirigidas a los servidores suelen implicar una transaccin de base de datos. Una situacin bastante comn consiste en enviar una solicitud para que se realice una bsqueda completa en una base de datos, encargndose a continuacin el servidor de dar formato a los resultados como pgina HMTL y enviar sta al explorador (por supuesto, si el cliente dispone de un mayor grado de inteligencia, gracias a la utilizacin de Java o de un lenguaje de script, pueden enviarse los datos en bruto y formatearlos en el extremo cliente, lo que sera ms rpido e impondra una menor carga de trabajo al servidor). Otro ejemplo: puede que queramos registrar nuestro nombre

en una base de datos para unimos a un grupo o realizar un pedido, lo que a su vez implica efectuar modificaciones en la base de datos. Estas solicitudes de base de datos deben procesarse mediante algn tipo de cdigo situado en el lado del cliente; es a este tipo de programas a los que nos referimos a la hora de hablar de programacin del lado del cliente. Tradicionalmente, la programacin del lado del cliente se llevaba a cabo utilizando Perl. Python, C-H-, o algn otro lenguaje para crear programas CG1, pero con el tiempo se han desarrollado otros sistemas ms sofisticados, entre los que se incluyen los servidores web basados en Java que permiten realizar todas las tareas de programacin del lado del servidor en lenguaje Java, escribiendo lo que se denomina servlets. Los servlets y sus descendientes, las pginas JSP, son dos de las principales razones por las que las empresas que desarrollan sitios web estn adoptando Java, especialmente porque dichas tecnologas eliminan los problemas derivados de tratar con exploradores que dispongan de capacidades diferentes. Los temas de programacin del lado del servidor se tratan en Thinking in Enterprise Java en el sitio web www.MindView.net.

A pesar de todo lo que hemos comentado acerca de Java y de Internet, Java es un lenguaje de programacin de propsito general, que permite resolver los mismos tipos de problemas que podemos resolver con otros lenguajes. En este sentido, la ventaja de Java no radica slo en su portabilidad. sino tambin en su programabilidad. su robustez, su amplia biblioteca estndar y las numerosas bibliotecas de otros fabricantes que ya estn disponibles y que continan siendo desarrolladas. Resumen

Ya sabemos cul es el aspecto bsico de un programa procedimental: definiciones de datos y llamadas a funciones. Para comprender uno de esos programas es preciso analizarlo, examinando las llamadas a funcin y utilizando conceptos de bajo nivel con el fm de crear un modelo mental del programa. sta es la razn por la que necesitamos representaciones intermedias a la hora de disear programas procedimentales: en si mismos, estos programas tienden a ser confusos, porque se utiliza una forma de expresarse que est ms orientada hacia la computadora que hacia el programa que se trata de resolver.

Como la programacin orientada a objetos aade numerosos conceptos nuevos, con respecto a los que podemos encontrar en un lenguaje procedimental. la intuicin nos dice que el programa Java resultante ser ms complicado que el programa procedimental equivalente. Sin embargo, la realidad

resulta gratamente sorprendente: un programa Java bien escrito es. generalmente, mucho ms simple y mucho ms fcil de comprender que un programa procedimental. Lo que podemos ver al analizar el programa son las definiciones de los objetos que representan los conceptos de nuestro espacio de problema (en lugar de centrarse en la representacin realizada dentro de la mquina), junto con mensajes que se envan a esos objetos para representar las actividades que tienen lugar en ese espacio de problema. Uno de los atractivos de la programacin orientada a objetos, es que con un programa bien diseado resulta fcil comprender el cdigo sin ms que leerlo. Asimismo, suele haber una cantidad de cdigo bastante menor, porque buena parte de los problemas puede resolverse reutilizando cdigo de las bibliotecas existentes.

La programacin orientada a objetos y el lenguaje Java no resultan adecuados para todas las situaciones. Es importante evaluar cules son nuestras necesidades reales y determinar si Java permitir satisfacerlas de forma ptima o si. por el contrario, es mejor emplear algn otro sistema de programacin (incluyendo el que en la actualidad estemos usando). Si podemos estar seguros de que nuestras necesidades van a ser bastante especializadas en un futuro prximo, y si estamos sujetos a restricciones especficas que Java pueda no satisfacer, resulta recomendable investigar otras alternativas (en particular, mi recomendacin sera echarle un vistazo a Python: vase wwM'.Pyrhon.org). Si decide, a pesar de todo, utilizar el lenguaje Java, al menos comprender, despus de efectuado ese anlisis, cules sern las opciones existentes y por qu resultaba conveniente adoptur la decisin que finalmente haya tomado.Todo es un objeto

2 Si hablramos un lenguaje diferente, percibiramos un mundo algo distinto.

Ludwig Wittgenstein (1889-1951)

Aunque est basado en C++ Java es un lenguaje orientado a objetos ms puro*.

Tanto C++ como Java son lenguajes hibridos. pero en Java los diseadores pensaron que esa hibridacin no era tan importante con en C++. Un lenguaje hbrido permite utilizar mltiples estilos programacin: la razn por la que O-*- es capaz de soportar la compatibilidad descendente con el

lenguaje (\ Puesto que C++ es un superconjunto del lenguaje C, incluye muchas de las caractersticas menos deseables de ese lenguaje, lo que hace que algunos aspectos del 0+ sean demasiado complicados.

El lenguaje Java presupone que el programador slo quiere realizar programacin orientada a objetos. Esto quiere decir que, antes de empezar, es preciso cambiar nuestro esquema mental al del mundo de la orientacin a objetos (a menos que ya hayamos efectuado esa transicin). La ventaja que se obtiene gracias a este esfuerzo adicional es la capacidad de programar en un lenguaje que es ms fcil de aprender y de utilizar que muchos otros lenguajes orientados a objetos. En este capitulo veremos los componentes bsicos de un programa Java y comprobaremos que (casi) todo en Java es un objeto. Los objetos se manipulan mediante referencias

Cada lenguaje de programacin dispone de sus propios mecanismos para manipular los elementos almacenados en memoria. En ocasiones, el programador debe ser continuamente consciente del tipo de manipulacin que se esta efectuando. Estamos tratando con el elemento directamente o con algn upo de representacin indirecta (un puntero en (. OC+-M. que haya que tratar con una sintaxis especial?

Todo esto se simplifica en Java. En Java, todo se trata como un objeto, utilizando una nica sintaxis coherente. Aunque trufamos todo como un objeto, los identificado res que manipulamos son en realidad referencias a objetos 4i Podramos imaginamos una TV' (el objeto) y un mando a distancia F.ste punto puede suscitar enconadadebates Hay personas que sostienen que claramente se traa de un puntero", pero cato estii presuponiendo una determinada implcmentacion subyacente. Asimismo, lu referencias en Java se parecen mucho mas sintctica monte a la relerenctas O-*- que a los punteros. fcn la primera edicin de este libro decid utilizar el termino descriptor porque las referencias O y las referencias Java tienen diferencias notables. Yo mismo provena del mundo del lenguaje Ct y no quera confundir a los programadores de GH-. que supona que constituiran la eran mayora de personas interesadas en el lenguaje Java. En la segunda edicin, decid que referencia" era el trmino ms comnmente utilizada), y que cualqucra que proviniera del inundo de (.+-* tbu a enfrentarse a problemas mucho ms gnu es que la terminologa de las referencias, por Jo que no tema sentido usar
4i

(la referencia); mientras dispongamos de esta referencia tendremos una conexin con la televisin, pero cuando alguien nos dice "cambia de canal" o "baja el volumen, lo que hacemos es manipular la referencia, que a su vez modifica el objeto. Si queremos movemos por la habitacin y continuar controlando la TV. llevamos con nosotros el mando a distancia/referencia, no la televisin.

una palabra distinta Sin embarco. Iiay persono* que estn en desacuerdo incluso con el termino referencia. En un determinado libio, pude leer que resulta completamente equivocado decir que Java soporta el paso por referencia", o que los identifica dores de los objeto* Java (de acuerdo con el autor del libro son en realidad 'referencias a objetos" Por lo que (contina el autor) todo se pasa en la prctica por valor Segn e*te autor, no se efecta un paso por referencia, sino que se "pasa una referencia a objeto por salar Podramos discutir acerca de la precisin de c-ta> complicadas cxplicacumcs. pero ere' que el enfoque que he adoptado en este libro implifica la compresin del concepto sm generar mngnn tipo de problema lio* puristas del lenguaje podran soktener que estoy mintiendo, pero a eso respondera que lo que estoy haciendo e* proporcionar una abstraccin apropiad.!l

Asimismo, el mando a distancia puede existir de manera independiente, sin necesidad de que exista una televisin. En otras palabras, el hecho de que dispongamos de una referencia no implica necesariamente que haya un objeto conectado a la misma. De este modo, si queremos almacenar una palabra o una frase podemos crear una referencia de tipo String String s

Pero con ello slo habremos creado la referencia a un objeto. Si decidiramos enviar un mensaje a s en este punto, obtendramos un error porque s no est asociado a nada (no hay televisin). Por tanto, una prctica ms segura consiste en inicia- lizar siempre las referencias en el momento de crearlas: String s = "asdf;

Sin embargo, aqu estamos utilizando una caracterstica especial de Java. Las cadenas de caracteres pueden inicializarse con un texto entre comillas. Normalmente, ser necesario emplear un tipo ms general de inicializacin para los restantes objetos. Es necesario crear todos los objetos

Al crear una referencia, lo que se desea es conectarla con un nuevo objeto. Para ello, en general, se emplea el operador ncw La palabra clave new significa: Crea un nuevo ejemplar de este tipo de objeto. Por tanto, en el ejemplo anterior podramos escribir: String s = new String("asdf") ;

Esto no slo dice: "Crea un nuevo objeto String". sino que tambin proporciona informacin acerca de cmo crear el objeto suministrando una cadena de caracteres inicial.

Por supuesto. Java incluye una pltora de tipos predefinidos, adems de String. Lo ms importante es que tambin podemos crear nuestros propios tipos. De hecho, la creacin de nuevos tipos es la actividad fundamental en la programacin Java, y eso es precisamente lo que aprenderemos a hacer en el resto del libro.

Los lugares de almacenamiento

Resulta til tratar de visualizar la forma en que se dispone la informacin mientras se ejecuta el programa: en particular, es muy til ver cmo est organizada la memoria. Disponemos de cinco lugares distintos en los que almacenar los datos:

1.

Registros. Se trata del tipo de almacenamiento ms rpido, porque se encuentra en un lugar distinto al de los dems tipos de almacenamiento: dentro del procesador. Sin embargo, el nmero de registros esta muy limitado, por lo que los registros se asignan a medida que son necesarios. No disponemos de 1111 control directo sobre los mismos, ni tampoco podremos encontrar en los programas que los registros ni siquiera existan (C y C++, por el contrario, permiten sugerir al compilador que el almacenamiento se haga en un registro).

2.

La pila. Esta zona se encuentra en el rea general de memoria de acceso aleatorio (RAM), pero el procesador proporciona un soporte directo para la pila, gracias al puntem Je pila. El puntero de pila se desplaza hacia abajo para crear nueva memoria y hacia arriba para liberarla. Se trata de una forma extraordinariamente rpida y eficiente de asignar espacio de almacenamiento, slo superada en rapidez por los registros. El sistema Java debe conocer, a la hora de crear el programa, el tiempo de vida exacto de todos los elementos que se almacenan en la pila. Esta restriccin impone una serie de lmites a la flexibilidad de los programas, por lo que aunque parte del almacenamiento dentro de Java se lleva a cabo en la pila (en concreto, las referencias a objetos), los propios objetos Java no se colocan nunca en la pila.

3.

El cmulo Es un rea de memoria de propsito general (tambin situada dentro de la RAM) en la que se almacenan todos los objetos Java. El aspecto ms atractivo del cmulo de memoria, a diferencia de la pila, es que el compilador no necesita conocer de antemano durante cunto tiempo se va a ocupar ese espacio de almacenamiento dentro del cmulo. Por tanto, disponemos de un alto grado de flexibilidad a la hora de utilizar el espacio de almacenamiento que el cmulo de memoria proporciona. Cada vez que hace falta un objeto, simplemente se escribe el cdigo para crearlo utilizando new, y el espacio de almacenamiento correspondiente en el cmulo se asigna en el momento de ejecutar el cdigo. Por supuesto, esa flexibilidad tiene su precio: puede que sea necesario un tiempo ms largo para asignar y liberar el espacio de almacenamiento del cmulo de memoria, si lo comparamos con el tiempo necesario para el almacenamiento en la pila (eso suponiendo que pudiramos crear objetos en la pila en Java, al igual que se hace en C+ +).

4.

Almacenamiento constante. Los valores constantes se suelen situar directamente dentro del cdigo de programa, lo que resulta bastante seguro, ya que nunca varan. En ocasiones, las

constantes se almacenan por separado, de forma que se pueden guardar opcionalmente en la memoria de slo lectura (ROM, readonly mvmon). especialmente en los sistemas integrados.

5.

Almacenamiento fuera de la RAM Si los datos residen fuera del programa, podrn continuar existiendo mientras el programa no se est ejecutando, sin que el programa tenga control sobre los mismos. Los dos ejemplos principales son los objetos stream, en los que los objetos se transforman en flujos de bytes. generalmente para enviarlos a otra mquina, los objetos persistentes en los que los objetos se almacenan en disco para que puedan conservar su estado incluso despus de terminar el programa. El Unco con estos tipos de almacenamiento consiste en transformar los objetos en algo que pueda existir en el otro medio de almacenamiento, y que. sin embargo, pueda recuperarse para transformarlo en un objeto normal basado en RAM cuando sea necesario. Java proporciona soporte para lo que se denomina persistencia ligera y otros mecanismos tales como JDBC e Hibemate proporcionan soporte ms sofisticado para almacenar y extraer objetos utilizando bases de datos.

Caso especial: tipos primitivos

Hay un grupo de tipos que se emplean muy a menudo en programacin y que requieren un tratamiento especial. Podemos considerarlos como tipos 'primitivos". La razn para ese tratamiento especial es que crear un objeto con new, una variable simple de pequeo tamao, no resulta muy eficiente, porque new almacena los objetos en el cmulo de memoria. Para estos tipos primitivos. Java utiliza la tcnica empleada en Cy C++; es decir, en lugar de crear la variable con new, se crea una variable 'automtica" que no es una referencia. La variable almacena el valor directamente y se coloca en la pila, por lo que resulta mucho ms eficiente.

Java determina el tamao de cada tipo primitivo Estos tamaos no cambian de una arquitectura de mquina a otra, a diferencia de lo que sucede en la mayora de los lenguajes. Esta invariabilidad de los tamaos es una de las razones por la que los programa Java son ms portables que los programas escritos en la mayora de los dems lenguajes

Todos los tipos numricos tienen signo, por lo que Java no podr encontrar ningn tipo sin signo,

El tamao del tipo boolean no est especificado de manera explcita; tan slo se define para que sea capaz de aceptar los valores literales true o false.

Las clases envoltorio para los tipos de datos primitivos permiten definir un objeto no primitivo en el cmulo de memoria que represente a ese tipo primitivo. Por ejemplo: char c 'x' ; Character ch new Characterle) ;

O tambin podra emplear: : Un ejemplo sera el conjunto de cadenas de caracteres Todas las cadenas literales y constantes que tengan un valor de tipo carcter se turnan de forma automtica y se asignan a un almacenamiento esttico especial.
7

Character ch = new Character('x*);

La caracterstica de Java SE5 denominada autoboxing permite realizar automticamente la conversin de un tipo primitivo a un tipo envoltorio: Character ch = 'x;

Y a la inversa: char c * ch;

En un captulo posterior veremos las razones que existen para utilizar envoltorios con los tipos primitivos. Arimtica de alta precisin

Java incluye dos clases para realizar operaciones aritmticas de alta precisin: Biglntegcr y BigDocimal. Aunque estas clases caen aproximadamente en la misma categora que las clases envoltorio, ninguna de las dos dispone del correspondiente tipo primitivo.

Ambas clases disponen de mtodos que proporcionan operaciones anlogas a las que se realizan con los tipos primitivos, lis decir, podemos hacer con un objeto Biglnteger o Hii>L)ecimal cualquier cosa que podamos hacer con una variable int o float: simplemente deberemos utilizar llamadas a mtodos en lugar de operadores. Asimismo, como sor ms complejas, las operaciones se ejecutarn ms lentamente En este caso, sacrificamos pane de la velocidad en aras de la precisin.

Biglnteger soporta nmeros enteros de precisin arbitraria. Esto quiere decir que podemos representar valores enteros de cualquier tamao sin perder ninguna informacin en absoluto durante las operaciones.

BiuDecimal se utiliza para nmeros de coma tija y precisin arbitraria. Podemos utilizar estos nmeros, por ejemplo, para realizar clculos monetarios precisos

Consulte la documentacin del kit JDK para conocer ms detalles acerca de los constructores y mtodos que se pueden invocar para estas dos clases. Matrices en Java

Casi todos los lenguajes de programacin soportan algn tipo de matriz. La utilizacin de matrices en C y C ' es peligrosa, porque dichas matrices son slo bloques de memoria Si un programa accede a la matriz fuera de su correspondiente hlo- que de memoria o utiliza la memoria antes de la inicializacin (lo cual son dos errores de programacin comunes), los resultados sern unprcdecibles.

Uno de los principales objetivos de Java es la seguridad, por lo que en Java no se presentan muchos de los problemas que aquejan a los programadores en C y C+-+. Se garantiza que las matrices Java siempre se inicialicen y que no se pueda acceder a ellas fuera de su rango autorizado. Las comprobaciones de rango exigen pagar el precio de gastar una pequea cantidad de memoria adicional para cada matriz, asi como de verificar el ndice en tiempo de ejecucin, pero se supone que el incremento en productividad y la mejora de la seguridad compensan esas desventajas (y Java puede en ocasiones optimizar csias operaciones).

Cuando se crea una matriz de objetos, lo que se crea realmente es una matriz de referencias, y cada una de e*.as matrices se inicializa automticamente con un valor especial que tiene su propia palabra clave: nuil. Cuando Java se encuentra un valor nuil, comprende que la referencia en cuestin no est apuntando a ningn objeto. Es necesario asignar un objeto a cada referencia antes de utilizarla, y si se intenta usar una referencia que siga teniendo el valor nuil, se informar del error en tiempo de ejecucin. De este modo, en Java se evitan los errores comunes relacionados con las matrices

Tambin podemos crear una matriz de valores primitivos. De nuevo, el compilador se encarga de garantizar la inicializacin. rellenando con ceros la memoria correspondiente a dicha matriz.

Hablaremos con detalle de las matrices en captulos posteriores.

Nunca es necesario destruir un objeto

En la mayora de los lenguajes de programacin, el concepto de tiempo de vida de una variable representa una parte significativa del esfuerzo de programacin. Cunto va a durar la variable? Si hay que destruirla, cundo debemos hacerlo? La confusin en lo que respecta al tiempo de vida de las variables puede generar una gran cantidad de errores de programacin, y en esta seccin vamos a ver que Java simplifica enormemente este problema al encargarse de realizar por nosotros todas las tareas de limpieza mbito

La mayora de los lenguajes procedimentales incluyen el concepto de mbito. El mbito determina tanto la visibilidad como el tiempo de vida de los nombres definidos dentro del mismo. En C, C-H- y Java, el mbito est determinado por la colocacin de las llaves 0- de modo que. por ejemplo:

( int x 12; // x ( int q = 96; // estn disoonibles tanto x como q

slo est disponible x // q est "fuera del mbito"


II

Una variable definida dentro de un mbito slo est disponible hasta que ese mbito termina.

Todo texto situado despus de los caracteres * y hasta el final de la lnea es un comentario.

El sangrado hace que el cdigo Java sea ms fcil de leer. Dado que Java es un lenguaje de formato libre, los espacios, tabuladoras y retornos de carro adicionales no afectan al programa resultante.

No podemos hacer lo siguiente, a pesar de que si es correcto en C y Ct-r:

< int x = 1 2 { int x * 96; // Ileqal

} J

El compilador nos indicara que la variable * ya ha sido definida Por tanto, no est permitida la posibilidad en C y C * de ocultar" una variable en un mbito ms grande, porque los diseadores de Java pensaron que esta caracterstica hacia los programa ms confusos. mbito de los objetos

Los objetos Java no tienen el mismo tiempo de vida que las primitivas. Cuando se crea un objeto Java asando new. ese objeto contina existiendo una vez que se ha alcanzado el final del mbito. Por tanto, si usamos:

{ String s = new String(1Ma string"); ) f / Fin del mbito

la referencia s desaparece al final del mbito. Sin embargo, el objeto String al que s estaba apuntando continuar ocupando memoria. En este fragmento de cdigo, no hay forma de acceder al objeto despus de alcanzar el final del mbito, porque la nica referencia a ese objeto est ahora fuera de mbito. En captulos posteriores veremos cmo pasar y duplicar una referencia a un objeto durante la ejecucin de un programa.

Como los objetos que creamos con new continuarn existiendo mientras queramos, hay toda una serie de problemas tpicos de programacin en C++ que en Java se desvanecen. En C++ no slo hay que asegurarse de que los objetos permanezcan mientras sean necesarios, sino que tambin es preciso destruirlos una \e/ que se lia terminado de usarlos.

listo suscita una cuestin interesante. Si los objetos continan existiendo en Java perpetuamente, qu es lo que evita llenar la memoria y hacer que el programa se detenga? ste es exactamente el tipo de problema que poda ocurrir en 0+f y para resolverlo Java recurre a una especie de varita mgica. Java dispone de lo que se denomina depurador de memoria, que examina todos los objetos que hayan sido creados con new y determina cules no tienen ya ninguna referencia que les apunte. A continuacin. Java libera la memoria correspondiente a esos objetos, de forma que pueda ser utilizada para crear otros objetos nuevos. Esto significa que nunca es necesario preocuparse de reclamar explcitamente la memoria. Basta con crear los objetos y, cuando dejen de ser necesarios, se borrarn automticamente. Esto elimina un cierto tipo de problema de programacin: las denominadas fugas de memoria que se producen cuando, en otros lenguajes, un programador se olvida de liberar la memoria. Creacin de nuevos tipos de datos: class

Si todo es un objeto, que es lo que determina cmo se compona y que aspecto tiene una clase concreta de objeto? Dicho de otro modo, qu es lo que establece el tipo de un objeto? Cabra esperar que existiera una palabra clave denominada utype" (tipo), y de hecho tendra bastante sentido Histricamente, sin embargo, la mayora de los lenguajes orientados a objetos han utilizado la palabra clave class para decir voy a definir cul es el aspecto de un nuevo tipo de objeto". La palabra clave class (que es tan comn que la escribiremos en negrita normalmente a lo largo del libro) va seguida

del nombre del nuevo tipo que queremos definir Por ejemplo: class ATypeName ( /* Aqu ira el cuerpo de la clase / )

Este cdigo permite definir un nuevo tipo, aunque en este caso el cuerpo de la clase slo incluye un comentario (las barras inclinadas y los asteriscos junto con el texto forman el comentario, lo que veremos en detalle ms adelante en este captulo). asi que no es mucho lo que podemos hacer con esta clase que acabamos de definir. Sin embargo, si que podemos crear un objeto de este tipo utilizando new: ATypeName a = new ATypeName {) ;

Si bien es verdad que no podemos hacer que ese objeto lleve a cabo ninguna tarea til (es decir, no le podemos enviar ningn mensaje interesante) hasta que definamos algunos mtodos para ese objeto. Campos y mtodos

Cuando se define una clase (y todo lo que se hace en Java es definir clases, crear objetos de esa clase y enviar mensajes a dichos objetos), podemos incluir dos tipos de elementos dentro de la clase: campos (algunas veces denominados miembros de datos) y mtodos (en ocasiones denominados funciones miembro). Un campo es un objeto de cualquier tipo con el que podemos comunicamos a travs de su referencia o bien un tipo primitivo. Si es una referencia a un objeto, hay que inicia- lizar esa referencia para conectarla con un objeto real (usando new, como hemos visto anteriormente).

Cada objeto mantiene su propio almacenamiento para sus campos: los campos normales no son compartidos entre los distintos objetos. Aqui tiene un ejemplo de una clase con algunos campos definidos: class DataOnly { int i; double d; boolean b;

Esta clase no hace nada, salvo almacenar una serie de datos. Sin embargo, podemos crear un objeto de esta clase de la forma siguiente: DataOnly data * new DataOnly O;

Podemos asignar valores a los campos, pero primero es preciso saber cmo refenmos a un miembro de un objeto. Para referimos a l. es necesario indicar el nombre de la referencia al objeto, seguida de un punto y seguida del nombre del miembro concreto dentro del objeto: objectReference.member

Por ejemplo. daca.i 47; data.d = =

1.1; data.b false;

Tambin es posible que el objeto contenga otros objetos, que a su vez contengan los datos que queremos modificar. En este caso, basta con utilizar los puntos correspondientes, como por ejemplo: myPlane.leftTank.capacity = 100;

La clase DataOnly no puede hacer nada ms que almacenar datos, ya que no dispone de ningn mtodo. Para comprender cmo funcionan los mtodos es preciso entender primero los conceptos de argumentos y valores de retorno. que vamos a describir en breve. Valores predeterminados de los miembros de tipo primitivo

Cuando hay un tipo de datos primitivo como miembro de una clase. Java garantiza que se le asignar un valor predeterminado en caso de que no se inicialice:

Los valores predeterminados son slo los valores que Java garantiza cuando se emplea la variable como miembro de una clase. Esto garantiza que las variables miembro de tipo primitivo siempre sean inicializadas (algo que C-H- no hace), reduciendo asi una fuente de posibles errores. Sin embargo, este valor inicial puede que no sea correcto o ni siquiera legal para el programa que se est escribiendo. Lo mejor es inicializar las variables siempre de forma explcita.

Esta garanta de iniciazacin no se aplica a las variables locales, aquellas que no son campos de clase. Por tanto, si dentro de la definicin de un mtodo tuviramos: int x;

Entonces x adoptara algn valor arbitrario (como en C y C++), no siendo automticamente imcializada con el valor cero. El programador es el responsable de asignar un valor apropiado antes de usar x. Si nos olvidamos. Java representa de todos modos una mejora con respecto a C-H-, ya que se obtiene un error en tiempo de compilacin que nos informa de que la variable puede no haber sido micializa (muchos compiladores de C^+ nos advienen acerca de la existencia de variables no inicializadas. pero en Java esta falta de inicializacion constituye un error). Mtodos, argumentos y valores de retorno

En muchos lenguajes (como C y C++), el trmino funcin se usa para describir una subrutina con nombre. El trmino ms comnmente utilizado en Java es el de mtodo, queriendo hacer referencia a una forma de llevar algo a cabo**. Si lo desea, puede continuar pensado en trminos de funciones; se trata slo de una diferencia sintctica, pero en este libro adoptaremos el uso comn del trmino mtodo dentro del mundo de Java.

Los mtodos de Java determinan los mensajes que un objeto puede recibir. Las panes fundamentales de un mtodo son el nombre, los argumentos, el tipo de retomo y el cuerpo. Esta seria la forma bsica de un mtodo: TipoRetorno NombreKetodof / LBta de argumentos */ ) ( ) /* Cuerpo dei mtodo * /

El tipo de retomo describe el valor devuelto por el mtodo despus de la invocacin. La lista de argumentos proporciona la lista de los tipos y nombres de la informacin que hayamos pasado al mtodo. El nombre del mtodo y la lista de argumentos (que forman lo que se denomina signatura del mtodo) identifican de forma univoca al mtodo.

Los mtodos pueden crearse en Java nicamente como parte de una clase. Los mtodos slo se pueden invocar para un objeto', y dicho objeto debe ser capa/ de ejecutar esa invocacin del mtodo. Si tratamos de invocar un miodo correcto para un objeto obtendremos un mensaje de error en tiempo de compilacin. Para invocar un mtodo para un objeto, hay que nombrar el objeto seguido de un punto, seguido del nombre del mtodo y de su lista de argumentos, como por ejemplo NombreObjeto.NombreMetodolargl, arg2, arg3';

Por ejemplo, suponga que tenemos un mtodo f( ) que no tiene ningn argumento y que devuelve una valor de tipo int Entonces, si tuviramos un objeto denominado a para el que pudiera invocarse f ) podriamos escribir: int x = a. f ();

El tipo del valor de retomo debe ser compatible con el tipo de x.

Este acto de invocar un mtodo se denomina comnmente enviar un mensaje a un objeto. En el ejemplo anterior, el mensaje es f() y el objeto es a. A menudo, cuando se quiere resumir lo que es la programacin orientada a objetos se suele decir que consiste simplemente en enviar mensajes a objetos. La lista de argumentos

La lista de argumentos del mtodo especifica cul es la informacin que se le pasa al mtodo. Como puede suponerse, esta informacin (como todo lo dems en Java) adopta la forma de objetos. Por tanto, lo que hay que especificar en la lista de argumentos son los tipos de los objetos que hay pasar y el nombre que hay que utilizar para cada uno. Como en cualquier otro caso dentro de Java en el que parece que estuv iramos gestionando objetos, lo que en realidad estaremos pasando son referencias 8 Sin embargo, el tipo de la referencia debe ser correcto. Si el argumento es de tipo String. ser preciso pasar un objeto String. porque de lo contrario el compilador nos dara un error.

Supongamos un cierto mtodo que admite un objeto String como argumento. Para que la compilacin se realice correctamente. la definicin que habra que incluir dentro de la definicin de la clase seria como la siguiente: int storage (Strmg s) { return a . length O * 2;

Con la excepcin usual de los tipos de daios especiales que ornes hemos mencionado tatolean, char, bytc, short, int, long, flout y douhte. I.n genera). sm embargo, lo que se pasan son objetos, lo que realmente quiere decir que se pasan referencias a objetos
8

Este mtodo nos dtce cuntos bytes se requieren para almacenar la informacin contenida en un objeto String concreto (el tamao de cada char en un objeto String es de 16 bits, o dos bytes, para soportar los caracteres Unicode). El argumento es de tipo Serinj y se denomina v Una ve? que se pasa s al mtodo, podemos tratarlo como cualquier otro objeto (podemos enviarle mensajes) Aqu, lo que se hace es invocar el mtodo lcngth( ). que es uno de los mtodos definidos para los objetos de tipo String; este mtodo devuelve el nmero de caracteres que hay en una cadena.

Tambin podemos ver en el ejemplo cmo se emplea la palabra clave return Esta palabra clave se encarga de dos cosas: en primer lugar, quiere decir "sal del mtodo, porque ya he terminado, en segundo lugar, si el mtodo ha generado un valor, ese valor se indica justo a continuacin de una instruccin return. en este caso, el valor de retomo se genera evaluando la expresin s.length( ) * 2.

Podemos devolver un valor de cualquier tipo que deseemos, pero si no queremos devolver nada, tenemos que especificarlo. indicando que el mtodo devuelve un valor de tipo void. He aqu algunos ejemplos: boolean flagO ( retura true; } double naturalLogBase() ( returrt 2.71S; ) void nothmgO ( retura; ) void nothing2 () {}

Cuando el tipo de retomo es void, la palabra clave return se utili/a slo para salir del mtodo, por lo que resulta necesaria una vez que se alcanza el final del mtodo. Podemos volver de un mtodo en cualquier punto, pero si el tipo de retomo es distinto de void, entonces el compilador nos obligar (mediante los apropiados mensajes de error) a devolver un valor del tipo apropiado, independientemente del lugar en el que salgamos del mtodo.

Llegados a este punto, podra parecer que un programa consiste, simplemente, en una sene de objetos con mtodos que aceptan otros objetos como argumentos y envan mensajes a esos otros objetos. Realmente, esa descripcin es bastante precisa, pero en el siguiente capftnlo veremos cmo llevar a cabo el trabajo detallado de bajo nivel, tomando decisiones dentro de un mtodo. Para los propsitos de este capitulo, el envo de mensajes nos resultar ms que suficiente. Construccin de un programa Java

Hay otras cuestiones que tenemos que entender antes de pasar a disear nuestro primer programa Java. Visibilidad de los nombres

Uno de los problemas en cualquier lenguaje de programacin es el de control de los nombres. Si utilizamos un nombre en un mdulo de un programa y otro programador emplea el mismo nombre en otro mdulo, cmo podemos distinguir un nombre del otro y cmo podemos evitar la colisin de los dos nombres? En C, este problema es especialmente significativo, porque cada programa es. a menudo, un conjunto manejable de nombres. Las clases C-*-+ (en las que se basan las clases Java) anidan las funciones dentro de clases para que no puedan colisionar con los nombres de funcin anidados dentro de otras clases. Sin embargo. O +- sigue permitiendo utilizar datos globales y funciones globales, as que las colisiones continan siendo posibles. Para resolver este problema, C++ introdujo los denominados espacios de nombres utilizando palabras clave adicionales

Java consigui evitar todos estos problemas adoptando un enfoque completamente nuevo. Para generar un nombre no ambiguo para una biblioteca, los creadores de Java emplean los nombres de dominio de Intcmci en orden inverso, ya que se garantiza que los nombres de dominio son unvocos. Puesto que mi nombre de dominio es MindView.net, una biblioteca de utilidades llamada foiblcs se denominara, por ejemplo, net.mindview.utilityJ'diblcs. Despus del nombre de dominio invenido, los puntos tratan de representar subdirectorios.

En Java l.0 y Java l . l . las extensiones de dominio com. edu. orj, net. etc., se escriban en maysculas por convenio* por lo que el nombre de la biblioteca seria NET.mindview.utility.foihles Sin embargo, durante el desarrollo de Java 2 se descubri que esto produca problemas, por lo que ahora los nombres completos de paquetes se escriben en minsculas.

Este mecanismo implica que todos los archivos se encuentran, automticamente, en sus propios espacios de nombres y que cada clase dentro de un archivo tiene un identificador unvoco: el lenguaje se encarga de evitar automticamente las colisiones de nombres. Utilizacin de otros componentes

Cada vez que se quiera utilizar una clase predefinida en un programa, el compilador debera saber cmo localizarla. Por supuesto, puede que la clase ya exista en el mismo archivo de cdigo fuente desde el que se la est invocando. En ese caso, basta con utilizar de manera directa la clase, an cuando esa clase no est definida hasta ms adelante en el archivo (Java elimina los problemas denominados de referencia anticipada).

Qu sucede con las clases almacenadas en algn otro archivo? Cabria esperar que el compilador fuera lo suficientemente inteligente como para localizar la clase, pero hay un pequeo problema. Imagine que queremos emplear una clase con un nombre concreto, pero que existe ms de una definicin para dicha clase (probablemente, definiciones distintas). O peor, imagine que est escribiendo un programa y que a medida que lo escribe aade una nueva clase a la biblioteca que entra en conflicto con el nombre de otra clase ya existente.

Para resolver este problema, es preciso eliminar todas las ambigedades potenciales. Esto se consigue informando al compilador Java de exactamente qu clases se desea emplear, utilizando para ello la palabra clave import import le dice al compilador que cargue un paquete, que es una biblioteca de clases (en otros lenguajes, una biblioteca podra es:ar compuesta por funciones y datos, as como clases, pero recuerde que todo el cdigo Java debe escribirse dentro de una clase).

La mayor parte de las veces utilizaremos componentes de las bibliotecas estndar Java incluidas con el compilador. Con estos componentes, no es necesario preocuparse sobre los largos nombres de dominio invenidos, basta con decir, por ejemplo: import java.til.ArrayList;

para informar al compilador que queremos utilizar la clase ArrayList de Java. Sin embargo, til contiene diversas clases, y podemos usar varias de ellas sin declararlas todas ellas explcitamente. Podemos conseguir esto fcilmente utilizando ** como comodn: import java.Util.;

Resulta ms comn importar una coleccin de clases de esta forma que importar las clases individualmente. La palabra clave static

Normalmente, cuando creamos una clase, lo que estamos haciendo es describir el aspecto de los objetos de esa clase y el modo en que stos van a comportarse. En la prctica, no obtenemos ningn objeto hasta que creamos uno empleando new. que es el momento en que se asigna el almacenamiento y los mtodos del objeto pasan a estar disponibles.

Existen dos situaciones en las que esta tcnica no resulta suficiente Una de ellas es cuando deseamos disponer de un nico espacio de almacenamiento para un campo concreto, independientemente del nmero de objetos de esa clase que se cree, o incluso si no se crea ningn objeto de esa clase. La otra situacin es cuando hace falta un mtodo que no est asociado con ningn objeto concreto de esa clase; en otras palabras, cuando necesitamos un mtodo al que podamos invocar incluso aunque no se cree ningn objeto.

Podemos conseguir ambos efectos utilizando la palabra clave static. Cuando decimos que algo es static. quiere decir que ese campo o mtodo concretos no estn ligados a ninguna instancia concreta de objeto de esa clase. Por tanto, an cuando nunca creramos un objeto de esa clase, podramos de todos modos invocar el mtodo static o acceder a un campo static. Con los campos y mtodos normales, que no tienen el atributo static. es preciso crear un objeto y usar esc objeto para acceder al campo o al mtodo, ya que los campos y mtodos que no son de tipo static deben conocer el objeto en particular con el que estn trabajando.9

Por supuesto, puesto que los mtodos ttatic no necesitan que se cree ningn objeto ames de utilizarlos, no pueden acceder directamente u ningn miembro o mtodo que no sea static. por el procedimiento de invocar simplemente esos otros miembros sin hacer referencia a un objeto nominado (ya que los miemhros y mtodos que no son static deben estar asociados con un objeto concreto).
9

Algunos lenguajes orientados a objetos utilizan los trminos ciatos de la clase y mtodos de la clase, para indicar que esos datos y mtodos slo existen para la clase como un todo, y no para ningn objeto concreto de esa clase. En ocasiones, en la literatura tcnica relacionada con Java tambin se utilizan esos trminos.

Para hacer que un campo o un mtodo sea static. basta con colocar dicha palabra clave antes de la definicin. Por ejemplo, el siguiente cdigo generara un campo static y le inicial izara: class StaticTest { static int i = 47;

Ahora, aunque creramos dos objetos StaticTest. existira nico espaciode almacenamiento para StaticTest.L Ambos

un

objetos compartiran el mismo StaticTest stl = new StaticTest st2 = new

campo i. Considere StaticTestO; StaticTestO;

elfragmento de cdigo siguiente:

En este punto, tanto stl.i como st2.i tienen el mismo valor, 47, ya que ambos hacen referencia a la misma posicin de memoria.

May dos formas de hacer referencia a una variable static. Como se indica en el ejemplo anterior, se la puede expresar a travs de un objeto, escribiendo por ejemplo. st2.i. Podemos hacer referencia a ella directamente a travs del nombre de la clase, lo que no puede hacerse con ningn nombre que no sea de tipo static. StaticTest .1^;

1:1 operador ++ incrementa en una unidad la variable. En este punto, tanto stl.i como st2. tendrn el valor 48.

La forma preferida de hacer referencia a una variable static es utilizando el nombre de la clase. Esto no slo permite enfatizar la naturaleza de tipo stalic de la variable, sino que en algunos casos proporciona al compilador mejores oportunidades para la optimizacin.

A los mtodos static se les aplica una lgica similar. Podemos hacer referencia a un mtodo static a travs de un objeto, al igual que con cualquier otro mtodo, o bien mediante la sintaxis adicional especial NomtireClase.meto- do( ). Podemos definir un mtodo static de manera a similar: class Incrementable ( static void incremento { StaticTest.-M-,10 }
10

til compilador Java y la documentacin suministrados por Sun tienden a cambiar de manera

Podemos ver que el mtodo ncrement() de Incrementable incrementa el dato i de tipo static utilizando el operador ++. Podemos invocar incronient( ) de la forma tpica a travs de un objeto: Incrementable sf = new Incrementable() ; B t . increment{ ) ;

O,dado que increment( ) es un mtodo static. podemos invocarlo directamente a travs de su clase: Incrementable. increment () ;

Aunque static. cuando se aplica a un campo, modifica de manera evidente la forma en que se crean los datos (se crea un dato global para la clase, a diferencia de los campos que no son static. para los que se crea un campo para cada objeto), cuando se aplica esa palabra clave a un mtodo no es Lan dramtico. Un uso importante de static para los mtodos consiste en permitimos invocar esos mtodos sin necesidad de crear un objeto. Esta caracterstica resulta esencial, como veremos, al definir el mtodo main( ), que es el punto de entrada para ejecutar una aplicacin.

frecuente, y el mejor lugar para obtenerlos es directamente en el sitio web de Sun. Descargndose esos archivos podr disponer siempre de la versin ms reciente

Nuestro primer programa Java

Finalmente, vamos a ver nuestro primer programa completo. Comenzaremos imprimiendo una cadena de caracteres y a continuacin la fecha, empleando la clase Date de la biblioteca estndar de Java. // HelloDace.java import j ava.ut i1.; public ciass HelloDate ( public static void main(String11 args) { System.out.println"Helio, it's: "); System.out.println(new Date());

} 1

Al principio de cada archivo de programa, es necesario incluir las instrucciones import necesarias para cargar las clases adicionales que se necesiten para el cdigo incluido en ese archivo. Observe que hemos dicho "clases adicionales. La razn es que existe una cierta biblioteca de clases que se carga automticamente en todo archivo Java: java,lan{>. Inicie el explorador web y consulte la documentacin de Sun (si no ha descargado la documentacin del kit JDK de http://javasun.coni, hgalo ahora. Observe que esta documentacin no se suministra con el JDK: es necesario descargarla por separado). Si consulta la lista de paquetes, podr ver todas las diferentes bibliotecas de clases incluidas con Java. Seleccione java.lang; esto har que se muestre una lista de todas clases que forman parte de esa biblioteca. Puesto que java.lang est implcitamente en todo archivo de cdigo Java, dichas clases estarn disponibles de manera automtica. No existe ninguna clase Date en ja\ a.lang. lo que quiere decir que es preciso importar otra biblioteca para usar dicha clase. Si no sabe la biblioteca en la que se encuentra una clase concreta o si quiere ver todas las clases disponibles, puede seleccionar Tree" en la documentacin de Java. Con eso. podr localizar todas las clases incluidas con Java. Entonces, utilice la (uncin Buscar* del explorador para encontrar Date. Cuando lo haga,

podra ver que aparece como java.util.Date. lo que nos permite deducir que se encuentra en la biblioteca til y que es necesario emplear la instruccin impurt java.util.* para usar Date.

Si vuelve atrs y selecciona java.lang y luego System, pudra ver que la clase System tiene varios campos; si selecciona out. descubrir que se trata de un objeto static PrintStream. Puesto que es de tipo static, no es necesario crear ningn objeto empleando new. El objeto out est siempre disponible, por lo que podemos usarlo directamente. Lo que puede hacerse con este objeto out est determinado por su tipo: PrintStream En la descripcin se muestra PrintStream como un hiper- vnculo, por lo que al hacer clic sobre l podr acceder a una lista de todos los mtodos que se pueden invocar para PrinrStream Son bastantes mtodos, v hablaremos de ellos mas adelante en el libro. Por ahora, el nico que nos interesa es println(), que en la prctica significa imprime en la consola lo que te voy a pasar y termina con un carcter de avance de linea. Asi. en cualquier programa Java podemos escribir algo como esto. System.out .printin ("Una cadena de caracteres*);

cuando queramos mostrar informacin a trav s de la consola.

El nombre de la clase coincide con el nombre del archivo. Cuando estemos creando un programa independiente como ste, una de las clases del archivo deber tener el mismo nombre que el propio archivo (el compilador se quejara si no lo hacemos asi). Dicha clase debe contener uu mtodo denominado man( ) con la siguiente signatura y tipo de retomo: public static void mainString argsJ (

La palabra clave puhlic quiere decir que el mtodo est disponible para todo el mundo (lo que describiremos en detalle en el Capimlo 6. ContraI Je acceso). El argumento de main( ) es una matriz de objetos String. En este programa, no se emplean los argumentos (args). pero el compilador Java insiste en que se incluya ese parmetro, ya que en l se almacenarn los argumentos de la lnea de comandos.

I.

a linea que imprime la fecha es bastante interesante: System.out.prrrtln (new Date) ;

El argumento es un objeto Date que slo se ha creado para enviar su valor (que se convierte automticamente al tipo String) a println( ) Una vez finalizada esta instruccin* dicho objeto Date resulta innecesario. > el depurador de memoria podr encargarse de l en cualquier momento. Nosoiros no necesitamos preocupamos de borrar el objeto.

Al examinar la documentacin del JDK en ////> (java stm.com, podemos ver que System tiene muchos mtodos que no> permiten producir muchos efectos interesantes (una de las caractersticas ms potentes de Java es su amplio conjunto de bibliotecas estndar). Por ejemplo: //; object/ShowProperties.java public class ShowProperties ( public static void main(StringI1 argsi ( System.aetPropertles(i . llst(System.out 1 ; System.out.printlnfSystem.getPropertyi "ussr.ame"}j System.out.println( System.aetProperty f"java.library.path"));

) ///:-

La primera linea de main( ) muestra todas las propiedades del sistema en que se est ejecutando el programa, por lo que nos proporciona la informacin del entorno. F.l mtodo list( ) enva los resultados a su argumento. System.out Ms adelante en el libro veremos que los resultados pueden enviarse a cualquier parte, como por ejemplo, a un archivo. Tambin podemos consultar una propiedad especfica, por ejemplo, en este caso, el nombre de usuario y java.library.path (un poco mas adelante explicaremos los comentarios bastante poco usuales situados al principio y al final de este fragmento de cdigo). Compilacin y ejecucin

Para compilar y ejecutar este programa, y cualquier otro programa de este libro, en primer lugar hay que tener un entorno de programacin Java Hay disponibles diversos entornos de programacin Java de otros fabricantes, pero en el libro vamos a suponer que el lector est utilizando el kit de desarrollo JDK (Java Developers K i t ) de Sun. el cual es gratuito. Si est utilizando otro sistema de desarrollo6, tendr que consultar la documentacin de dicho sistema para ver cmo compilar y ejecutar los programas.

El compilador jik.es de IBM es una alternativa bailante comn, y es Mgnificativamente tnt( rpido que Jova C de Sun (aunque se estn construyendo ynipo* de archivos utilizando Ant. la diferencia no c% muy significativa) Tambin hay diverso* proyectos de cdigo fuente abierto para crear compiladores Java, entornos de ejecucin y biblioteca.
6

Acceda a Internet y vaya a http; iuya.sun.com. All podra encontrar informacin y vnculos que le llevaran a tra\s del proceso de descarga e instalacin del JDK para su plataforma concreta.

Una vez insudado el JDK. y una vez modificada la informacin de ruta de la computadora para que esta pueda encontrar javac y java, descargue y desempaquete el cdigo fuente correspondiente a este libro (puede encontrarlo en u ni A JindYU'w.net). listo creara un subdirectono para cada capitulo del libro. Acceda al subdirectorio objecis v escriba. Javac HelloDate.java

Este comando no debera producir ninguna respuesta. Si obtiene algn tipo de mensaje de error querr decir que no ha instalado el JDK correctamente y tendr que investigar cul es el problema.

Por el contrario, si lo nico que puede ver despus de ejecutar el comando es una nueva linca de comandos, podr escribir java HelioDate y obtendr el mensaje y la fecha como salida.

Puede utilizar este proceso para compilar V ejecutar cada uno de los programas de este libro. Sin embargo, podra ver que el cdigo fuente de este libro tambin dispone de un archivo denominado huld.xml en cada capitulo, que contiene comandos Ant" para construir automticamente los archivos correspondientes a ese capitulo. Los archivos de generacin y Ant (incluyendo las instrucciones para descargarlos) se describen ms en detalle en el suplemento que podr encontrar en http://\1ind\te\v.net/Books/BetterJava. pero una vez que haya instalado Ant (desde http://iakaria.apache.org/ant) basta con que escriba ant en la linea de comandos para compilar y

ejecutar los programas de cada captulo. Si no ha instalado Ant todava, puede escribir los comandos javac y java a mano. Comentarios y documentacin embebida

Existen dos tipos de comentarios en Java. El primero de ellos es el comentario de estilo C heredado de C++. Estos comentarios comienzan con /* y continan, posiblemente a lo largo de varias lnea. Itasta encontrar */. Muchos programadores empiezan cada linea de un comando multilinca con un *. por lo que resulta bastante comn ver comentarios como ste: / Este comentario
* *

ocupa varias lineas

Recuerde, sin embargo, que en todo lo que hay entre I* y */ se ignora, por lo que no habra ninguna diferencia si dijramos: / Este comentario ocupa varias lineas /

El segundo tipo de comentario proviene de C-H*. Se irata del comentario monolinea que comienza con H y continua hasta el final de la linea. Este tipo de comentario es bastante cmodo y se suele utilizar a menudo debido a su sencillez. No es necesario desplazarse de un sino a otro del teclado para encontrar el carcter i y luego el carcter * (en su lugar, se pulsa la misma tecla dos veces), y tampoco es necesario cerrar el comentario. Asi que resulta bastante habitual encontrar comentarios de este estilo: // ste es un comentario monolinea. Documentacin mediante comentarios

Posiblemente el mayor problema de la larea de documentar el cdigo es el de mantener dicha documentacin. Si la documentacin y el cdigo estn separados, resulta bastante tedioso modificar la documentacin cada vez que se cambia el cdigo. La solucin parece simple: integrar el cdigo con la documentacin. La forma ms fcil de hacer esto es incluir todo en un mismo archivo. Sin embargo, para ello es necesario disponer de una sintaxis especial de comentarios que permita marear la documentacin, as como de una herramienta para extraer dichos comntanos y formatearlos de manera til, listo es precisamente lo que Java ha hecho.

La herramienta para extraer los comentarios se denomina Javadoc, y forma parte de la instalacin del JDK. Utiliza parte de la tecnologa del compilador Java para buscar los marcadores especiales de comentarios incluidos en el programa No slo extrae la informacin sealada por dichos marcadores, sino que tambin extrae el nombre de la clase o el nombre del mtodo asociado al comentario. De esta forma, podemos generar una documentacin de programa bastante digna con una cantidad mnima de trabajo.

La salida de Javadoc es un archivo HTML que se puede examinar con el explorador web. Javadoc permite crear y mantener un nico archivo fuente y generar automticamente una documentacin bastante til. Gracias a Javadoc. disponemos de un estndar bastante sencillo para la creacin de documentacin, por lo que podemos esperar, o incluso exigir, que todas las bibliotecas Java estn documentadas.

Adems, podemos escribir nuestras propias rutinas de gestin Javadoc, denominadas docels, si queremos realizar operaciones especiales con la informacin procesada por Javadoc (por ejemplo, para generar la salida en un formato distinto). Los doclets se explican en el suplemento que podra encontrar en http ://AfindVlew.net/Bnoks/BetterJava.

Lo que sigue es simplemente una introduccin y una breve panormica de los fundamentos de Javadoc. En la documentacin del JDK podr encontrar una descripcin ms exhaustiva Cuando desempaquete la documentacin, vaya al subdirec- torio tooldocs (o haga clic en el vinculo tooldocs"). Sintaxis

Todos los comandos de Javadoc estn incluidos en comentarios que tienen el marcador /**. Esos comentarios terminan con */ como es habitual Existen dos formas principales de utilizar Javadoc: mediante HTML embebido o mediante marcadores de documentacin". Los marcadores de documentacin autnomos son comandos que comienzan con (u ' y se incluyen al principio de una linea de comentarios (sin embargo, un carcter inicial sera ignorado). Los marcadores de documentacin en lnea pueden incluirse en cualquier punto de un comentario Javadoc y tambin comienzan con *(&/. pero estn encenados entre llaves

Existen tres tiposr-' de documentacin de comentarios, que se corresponden con el elemento al que el comentario precede: una clase, un campo o un mtodo. En otras palabras, el comentario de clase aparece justo antes de la definicin de la clase, el comentano de campo aparece justo antes de la definicin de un campo y e! comentario de un mtodo aparece justo antes de la definicin del mismo. He aqu un sencillo ejemplo: //: object/Documentationl .java /** Comentario de clase */ public class Documentationl {

/** Comentario de campo / public int i; /* Comentario de mtodo public void () {)

) ///t-

Observe que Javadoc slo procesar la documentacin de los comentarios para los miembros public y proteeted. Los comntanos para los miembros prvate y los miembros con acceso de paquete ( vase el Capitulo 6, Control de acceso) se ignoran, no generndose ninguna salida para ellos (sin embargo, puede utilizar el indicador prvate para incluir tambin la documentacin de los miembros prvate). Esto tiene bastante sentido, ya que slo los miembros de tipo public y proteeted estn disponibles fuera del archivo, por lo que esto se ajusta a la perspectiva del programador de clientes.

La salida del cdigo anterior es un archivo HTML que tiene el mismo formato estndar que el resto de la documentacin Java, por lo que los usuarios estarn acostumbrados a ese formato y podrn navegar fcilmente a travs de las clases que hayamos definido. Merece la pena introducir el ejemplo de cdigo anterior, procesarlo mediante Javadoc y observar el archivo HTML resultante, para ver el tipo de salida que se genera.

HTML embebido

Javadoc pasa los comntanos HTML al documento HTML generado. F.sto nos permite utilizar completamente las caractersticas HTML; sin embargo, el motivo principal de uso de ese lenguaje es dar formato al cdigo, como por ejemplo en //: object/Documentation2. java /**

<pre> System.out.println(new Date());

</p ro */

///:-

Tambin podemos usar edigo HTML como en cualquier otro documento web. para dar formato al texto normal de las descripciones: //: object/Documentation3. java /**

Se puede <em>incluso</em> insertar una lista: <ol> <ii Elemento uno <li> Elemento dos

<l> Elemento tres </ol>

///:-

Observe que. dentro del comentario de documentacin, los asteriscos situados al principio de cada lnea son ignorados por Javadoc. junto con los espacios iniciales Javadoc reformatea todo para que se adapte al estilo estndar de la documentacin. No utilice encabezados como <h1> o <hr> en el HTML embebido, porque Javadoc inserta sus propios encabezados y los que nosotros incluyamos interferirn con ellos.

Puede utilizarse HTML embebido en iodos los tipos de comemanos de documentacin: de clase, de campo y de mtodo.

Algunos marcadores de ejemplo

He aqui algunos de los marcadores Javadoc disponibles para la documentacin de cdigo. Antes de tratar de utilizar Javadoc de forma seria, consulte la documentacin de referencia de Javadoc dentro de la documentacin del JDK. para \cr las diferentes formas de uso de Javadoc. @see

F.ste marcador permite hacer referencia a la documentacin de otras clases. Javadoc generar el cdigo HTML necesario, hipervmculando los marcadores (a see a los oros fragmentos de documentacin. Las formas posibles de uso de este marcador son: @see nombreclase ^see nombreclase-completamente-cualif i cado r.-see norabreclase-completamente-cualificado fcnorabre-mtodo

Cada uno de estos marcadores aade una entrada See Also (Vase tambin) hipervinculada al archivo de documentacin generado. Javadoc no comprueba los hipervinculos para ver si son vlidos. {@link paquete.clasemiembro etiqueta}

Muy similar a (q see. excepto porque se puede utilizar en linea y emplea la etiqueto como texto del hipervncuk), en lugar de "See Also.

{@docRoot}

Genera la ruta relativa al directorio ra/ de la documentacin. Resulta til para introducir hipervinculos explcitos a pginas del rbol de documentacin. {@inheritDoc}

Este indicador hereda la documentacin de la clase base ms prxima de esta clase, insertndola en el comentario del documento actual @version

Tiene la forma: versin informacin-versin

en el que Informacin-versin es cualquier informacin significativa que queramos incluir. Cuando se aade el indicador a versin en la linea de comandos Javadoc. la informacin de versin ser mostrada de manera especial en la documentacin HTML generada.

@author

Tiene la forma: author informacin-autor

donde informacin-autor es, normalmente, nuestro nombre, aunque tambin podramos incluir nuestra direccin de correo electrnico o cualquier otra informacin apropiada. Cuando se incluye el indicador (aauthor en la linea de comandos Javadoc. se inserta la informacin de autor de manera especial en la documentacin HTML generada.

Podemos incluir mltiples marcadores de autor para incluir una lista de autores, pero es preciso poner esos marcadores de forma consecutiva. Toda la informacin de autor se agrupar en un nico prrafo dentro del cdigo HTML generado. @since

Este marcador permite indicar la versin del cdigo en la que se empez a utilizar una caracterstica concreta. En la documentacin HTML de Java, se emplea este marcador para indicar que versin del JDK se est utilizando.

@param

Se utiliza para la documentacin de mtodos y tiene la forma: jjparam nombreparmetro descripcin

donde nombre-parmetro es el identificador dentro de la lista de parmetros del mtodo y descripcin es un texto que puede continuar en las lineas siguientes. La descripcin se considera terminada cuando se encuentra un nuevo marcador de documentacin. Puede haber mltiples marcadores de este tipo, normalmente uno por cada parmetro. @return

Se utiliza para documentacin de mtodos y su formato es el siguiente: Sreturn descripcin

donde descripcin indica el significado del valor de retomo. Puede continuar en las lneas siguientes.

@throws

Las excepciones se estudian en el Captulo 12, Tratamiento Je errores mediante excepciones. Por resumir, se trata de objetos que pueden ser generados en un mtodo si dicho mtodo falla. Aunque slo puede generarse un objeto excepcin cada vez que invocamos un mtodo, cada mtodo concreto puede generar diferentes tipos de excepciones, todas las cuales habr que describir, por lo que el formato del marcador de excepcin es: sthrows nombre-case-completamente-cualificado descripcin

donde nombiV-clase-completamente-cualiftcado proporciona un nombre no ambiguo de una clase de excepcin definida en alguna otra parte y descripcin (que puede ocupar las siguientes lineas) indica la razn por la que puede generarse este tipo concreto de excepcin despus de la llamada al mtodo. @deprecated

Se utili/a para indicar caractersticas que han quedado obsoletas debido a la introduccin de alguna otra caracterstica mejorada. Este marcador indicativo de la obsolescencia recomienda que no se utilice ya esa caracterstica concreta, ya que es probable que en el futuro sea eliminada Un mtodo marcado como (a depreeated hace que el compilador genere una advertencia si se usa. En Jav a SE5, el marcador Javadoc (a depreeated ha quedado sustituido por la anotacin (a Depreeated (hablaremos de este tema en el Captulo 20. Anotaciones).

Ejemplo de documentacin

Me aqu de nuevo nuestro primer programa Java, pero esta vez con los comentarios de documentacin incluidos: //: object/HelloDate.java import j ava.til.; '** El primer programa de ejemplo del libro.

Muestra una cadena de caracteres y la fecha actual. author Bruce Eckel wauthor www.MindView.net

aversin 4.0 */ public class HelloDate { / * * Punto de entrada a la clase y a la aplicacin.


*

sparam args matriz de argumentos de cadena

* throws exceptions No se generan excepciones */

public static void main(String(] args) { System.out.println("Helio, it's: "); System.out.println(new Date(J);

} ) / Output: (55% match) Helio, it's: Wed Oct 05 14:39:36 MDT 2005 ///:-

a primera linea del archivo utiliza una tcnica propia del autor del libro que consiste en incluir V/f como marcador especial en la linea de comentarios que contiene el nombre del archivo fuente. Dicha lnea contiene la informacin de ruta del archivo (object indica este captulo) seguida del nombre del archivo. La ltima lnea tambin termina con un comentario, y ste (7//:-*) indica el final del listado de cdigo fuente, lo que permite actualizarlo automticamente dentro del texto de este libro despus de comprobarlo con un compilador y ejecutarlo.
1.

El marcador /* Output: indica el comienzo de la salida que generar este archivo. Con esta forma concreta, se puede comprobar automticamente para verificar su precisin. En este caso, el texto (55% match) indica al sistema de pruebas que la salida ser bastante distinta en cada sucesiva ejecucin del programa, por lo que slo cabe esperar un 55 por ciento de correlacin con la salida que aqu se muestre. La mayora de los ejemplos del libro que generan una salida contendrn dicha salida comentada de esta forma, para que el lector pueda ver la salida y saber que lo que ha obtenido es correcto. Estilo de codificacin

El estilo descrito en el manual de convenios de cdigo para Java. Code Conventions for /he Java PmgrammingLanguager, consiste en poner en mayscula la primera letra del nombre de una clase. Si el nombre de la clase est compuesto por varias ' Pam ahonur espacio tanto en el libro como en las presentacin de los seminario* no hemos podido seguir todos Jos directrices que se marcan en ese icxlo. per el lector podrA ver que el citilo empleado en el libro %c ajusia lo mximo posible al estandar recomendado en Java

palabras, stas se escriben juntas (es decir, no se emplean guiones bajos para separarlas) y se pone en mayscula la primera letra de cada una de las palabras integrantes, como en: class AIlTheColorsOfTheRainbow { // ...

Para casi todos los dems elementos, como por ejemplo nombres de mtodos, campos (variables miembro) y referencias a objetos, el estilo aceptado es igual que para las clases, salvo porque la primera letra del identificador se escribe en minscula, por ejemplo: class AIlTheColorsOfTheRainbow | int anlntegerRepresentingColors; void changeTheHueCfTheColorint newHue) { // ...

//

tan largos, asi que procure que su longitud no sea excese ajusta a las directrices de ubicacin de llaves de aper-

Recuerde que el usuario se ver obligado a escribir estos nombres siva.

F.1 cdigo Java que podr encontrar en las bibliotecas Sun tambin tura y cierre que hemos utilizado en este libro. Resumen

El objetivo de este capitulo es explicar los conceptos mnimos sobre Java necesarios para entender cmo se escribe un programa sencillo. Hemos expuesto una panormica del lenguaje y algunas de las ideas bsicas en que se fundamenta. Sin embargo, los ejemplos incluidos hasta ahora tenan todos ellos la forma haga esto, luego lo otro y despus lo de ms all. En los dos captulos siguientes vamos a presentar los operadores bsicos utilizados en los programas Java, y luego mostraremos como controlar el flujo del programa.

Ejercicios

Normalmente, distribuiremos los ejercicios por lodo el capitulo, pero en ste estbamos aprendiendo a escribir programas

bsicos, por lo que decidimos dejar los ejercicios para el final.

El nmero entre parntesis incluido detrs del nmero de ejercicio es un indicador de la dificultad del ejercicio dentro de

una escala de 1-10.

Puede encontrar las soluciones a los ejercicios seleccionados en el documento electrnico The Thmking in Java Annotated Soluiion Guete, a la venta en invw.AJindllew.net.

Ejercicio 1: (2) Cree una clase que contenga una variable int y otra char que no estn inicializadas e imprima sus valo

res para verificar que Java se encarga de realizar una inicializacin predeterminada.

Ejercicio 2: (I) A partir del ejemplo HclloDate.java de este capitulo, cree un programa helio, world que simplemen

te muestre dicha frase. Slo necesitar un mtodo dentro de la clase, (el mtodo main" que se ejecuta al arrancar el programa). Acurdese de definir este mtodo como static y de incluir la lista de argumentos, incluso aunque no la vaya a utilizar Compile el programa con javac y ejectelo con java. Si est utilizando otro entorno de desarrollo distinto de JDK. averige cmo compilar y ejecutar programas en dicho entorno.

Ejercicio 3: (l) Recopile los fragmentos de cdigo relacionados con ATypeNante y transfrmelos en un programa que

se pueda compilar y ejecutar

Ejercicio 4: (I) Transforme los fragmentos de cdigo DataOnly en un programa que se pueda compilar y ejecutar.

Ejercicio 5:(I) Modifique el ejercicio anterior de modo que los valores de los datos en DataOnly se asignen e impri man en main( ).

Ejercicio 6: (2) Escriba un programa que incluya e invoque el mtodo storage( ) definido como fragmento de cdigo

en el capitulo.

Ejercicio 7: funcional.

(1J Transforme los fragmentos de cdigo Incrcmcntable en un programa

Ejercicio 8: (3) Escriba un programa que demuestre que independientemente de cuntos objetos se creen de una clase

concreta, slo hay una nica instancia de un campo static concreto definido dentro de esa clase.

Ejercicio 9: (2) Escriba un programa que demuestre que el mecanismo automtico de conversin de tipos funciona

para todos los tipos primitivos y sus envoltorios.

Ejercicio 10: (2) Escriba un programa que imprima tres argumentos extrados de la lnea de comandos. Para hacer esto,

necesitar acceder con el ndice correspondiente a la matriz de objetos String extrada de la lnea de comandos.

Ejercicio 11: (l) Transforme el ejemplo AUTheColorsOfrheRainbow programa que se pueda compilar y eje

en un

cutar.

Ejercicio 12: (2) Localice el cdigo para la segunda versin de HclluDutc.java. que es el ejemplo simple ci documen

tacin mediante comentarios. Ejecute Javadoc con ese archivo y compruebe los

resultados con su explorador web. Ejercicio 13: (I) Procese Dcumentationl.java. Documentation2.java y Documcntation3.jaxa con Javadoc Ven-

fique la documentacin resultante con su explorador web.

Ejercicio 14: anterior.

< l > Aada una lista HTML de elementos a l3 documentacin del ejercicio

Ejercicio 15: (l) Tome el programa del Ejercicio 2 y adale comentarios de documentacin. Extraiga esos comntanos

de documentacin Javadoc para generar un archivo HTML y visualcelo con su explorador web.

Ejercicio 16: (I) En el Capitulo 5, metalizacin v limpieza, localice el ejemplo Overlnading.java y aada documenta

cin de tipo Javadoc. Extraiga los comentarios de documentacin Javadoc para generar un archivo HTML y visualcelo con su explorador web.Operadores

En el nivel inferior* los datos en Java se manipulan utilizando operadores.

Puesto que Java surgi a partir de C++. la mayora de estos operadores les sern familiares a casi todos los programadores de C y C-H- Java ha aadido tambin algunas mejoras y ha hecho algunas simplificaciones.

Si est familiarizado con la sintaxis de C o C-H-, puede pasar rpidamente por este captulo y el siguiente, buscando aquellos aspectos en los que Java se diferencie de esos lenguajes. Por el contrario, si le asaltan dudas en estos dos captulos, repase el seminario multimedia Thinking in C, que puede descargarlo gratuitamente en wwto.MindView.net. Contiene conferencias, presentaciones, ejercicios y soluciones especficamente diseados para familiarizarse con los fundamentos necesarios para aprender Java.

Instrucciones simples de impresin

En el captulo anterior, ya hemos presentado la instruccin de impresin en Java System.out.println("Un montn de texto que escribir");

Puede observar que esto no es slo un montn de texto que escribir (lo que quiere decir muchos movimientos redundantes de los dedos), sino que tambin resulta bastante incmodo de leer. La mayora de los lenguajes, tanto antes como despus de Java, han adoptado una tcnica mucho ms sencilla para expresar esta instruccin de uso tan comn.

lin el Capitulo 6. Control de acceso se presenta el concepto de importacin esttica aadido a Java SE5, y se crea una pequea biblioteca que permite simplificar la escritura de las instrucciones de impresin. Sin embargo, no es necesario comprender los detalles para comenzar a utilizar dicha biblioteca. Podemos reescribir el programa del ltimo capitulo empleando esa nueva biblioteca: //: operators/HelloDate.java import java.Util.*; import static net .mj.ndview.util. Print. ; public class HelloDate ( public static void maintStringU args) { print("Helio, it's: "); print(new Date<)) ;

} ) / Output: (55% match) Helio, it's: Wed Oct 05 14:39:05 MDT 2005 ///:-

Los resultados son mucho ms limpios. Observe la insercin de la palabra clave static en la segunda instruccin import.

Para emplear esta biblioteca, es necesario descargar el paquete de cdigo del libro desde www.MindView.net o desde alguno de los sitios espejo Descomprima el rbol de cdigo y aada el directorio raz de dicho rbol de cdigo a la variable de entorno CLASSPATH de su computadora (ms adelante proporcionaremos una introduccin completa a la variable de ruta CLASSPATH, pero es muy probable que el lector ya est acostumbrado a lidiar con esa variable. De hecho, es una de las batallas ms comunes que se presentan al intentar programar en Java)

Aunque la utilizacin de nct.mindview.util.Print simplifica enormemente la mayor pane del codigo. su uso no est justificado en todas las ocasiones. Si slo hay unas pocas instrucciones de impresin en un programa, es preferible no incluir la instruccin imporf y escribir el comando completo System.out.println( ).

Ejercicio 1: (l) F.scriba un programa que emplee tanto la forma "corta" como la normal de la instruccin de impresin. Utilizacin de los operadores Java

Un operador toma uno o ms argumentos y genera un nuevo valor. Los argumentos se incluyen de forma distinta a las llamadas normales a mtodos, pero el efecto es el mismo. La suma, el operador ms unano [+), la resta y el operador menos unariu ( ). la multiplicacin (*). la divisin (/) y la asignacin (=} funcionan de forma bastante similar a cualquier otro lenguaje de programacin.

Todos los valores producen un valor a partir de sus operandos Adems, algunos operadores cambian el valor del operando,

lo que se denomina efecto colateral. El uso ms comn de los operadores que modifican sus operandos consiste precisamente en crear ese efecto colateral, pero resulta conveniente pensar que el valor generado tambin esta disponible para nuestro uso. al igual que sucede con los operadores que no tienen efectos colaterales.

Casi todos los operadores funcionan nicamente con primitivas. Las excepciones son '=*. =* y *!=*. que funcionan con todos los objetos (y son una fuente de confusin con los objetos). Adems, la clase String sopona y *+=\ Precedencia

La precedencia de los operadores define la forma en que se evala una expresin cuando hay presentes varios operadores. Java tiene una serie de reglas especficas que determinan el orden de evaluacin. La ms fcil de recordar es que la multiplicacin y la divisin se realizan antes que la suma y la resta. Los programadores se olvidan a menudo de las restantes reglas de precedencia, as que conviene utilizar parntesis para que el orden de evaluacin est indicado de manera explcita. Por ejemplo, observe las instrucciones (1) y (2): //: operacors/Precedence.java public ciass Precedence ( System.out.println (**a ) } / OUtpUt: a = 5 b 1 *///:- M * a M b = M * b) :

Estas instrucciones tienen aspecto similar, pero la salida nos muestra que sus significados son bastante distintos, debido a la utilizacin de parntesis.

Observe que la instruccin System.out.println( ) incluye el operadorEn este contexto. *+* significa concatenacin de cadenas de caracteres y en caso necesario, conversin de cadenas de caracteres. Cuando el compilador ve un objeto String seguido de *+' seguido de un objeto no String. intenta convenirlo a un objeto String. Como puede ver en la salida, se ha efectuado conectamente la conversin de int a String para a y b. Asignacin

La asignacin se realiza con el operador =. Su significado es: Toma el valor del lado derecho, a menudo denominado rvo- lor, y cpialo en el lado izquierdo (a menudo denominado Ivalor)". Un rvalor es cualquier constante, variable o expresin que genere un valor, pero un Ivalor debe ser una variable detenninada, designada mediante su nombre (es decir, debe existir un espacio fsico para almacenar el valor). Por ejemplo, podemos asignar un valor constante a una variable: pero no podemos asignar nada a un valor constante, ya que una constante no puede ser un Ivalor (no podemos escribir 4 - a;).

La asignacin de primitivas es bastante sencilla. Puesto que la primitiva almacena el valor real y no una referencia a cualquier objeto, cuando se asignan primitivas se asigna el contenido de un lugar a otro. Por ejemplo, si escribimos a = b para primitivas, el contenido de b se copia en a Si a continuacin modificamos a. el valor de b no se ver afectado por esta modificacin. Como programador es lo que cabria esperar en la mayora de las situaciones.

Sin embargo, cuando asignamos objetos, la cosa cambia. Cuando manipulamos un objeto lo que manipulamos es la referencia, asi que al asignar de un objeto a otro*', lo que estamos haciendo en la prctica es copiar una referencia de un lugar a otro. Esto significa que si escribimos c - d para sendos objetos, lo que al final tendremos es que tanto c como d apuntan al objeto al que slo d apuntaba originalmente. He aqu un ejemplo que ilustra este comportamiento: ' : operators/As9ignment.java // La asignacin de objetos tiene su truco. import static net.mindview.til.Print. *; class Tank ( int level;

) public class Assignment ( public static void main(StringH args) { Tank ti = new Tank( ) ; Tank t2 - new Tank (); ti.level = 9; t2. level = 4 7? print ("1: ti.level: N. t.2. level -. ti = t2; print("2: ti.level: " ti.level + H, t2. level: ti.level = 27; print ("3:ti. level: ", t2.level: " " * + ti.level 4t2.level); M -f t2. level); " " t ti.level t2. level) ;

} } /* Output: 1: ti.level: 9, t2.level: 47 2: M.1pv17. t2.level: 47 3: ti.level: 27, t2.level: 27 * / / / ; -

La clase Tank es simple, y se crean dos instancias de la misma itl y t2) dentro de mafn( ). Al campo level de cada objeto Tank se le asigna un valor distinto, luego se asigna t2 a ti. y despus se modifica ti. En muchos lenguajes de programacin esperaramos que II y t2 fueran independientes en todo momento, pero como hemos asignado una referencia, al cambiar el objeto 11 se modifica tambin el objeto t2. Esto se debe a que tanto 11 como (2 contienen la misma referencia, que est apuntada en el mismo objeto (la referencia original contenida en (1, que apuntaba al objeto que tenia un valor de 9. fue sobrees- crita durante la asignacin y se ha perdido a todos los efectos; su objeto ser eliminado por el depurador de memoria).

ste fenmeno a menudo se denomina creacin de alias. y representa una de las caractersticas fundamentales del modo en que Java trabaja con los objetos. Pero, qu sucede si no queremos que las dos referencias apunten al final a un mismo objeto? Podemos reescribir la asignacin a otro nivel y utilizar:
F.

ti.level = t2.level;

Esto hace que se mantengan independientes los dos objetos, en lugar de descartar uno de ellos y asociar ti y t2 al mismo objeto. Ms adelante veremos que manipular los campos dentro de los objetos resulta bastante confuso y va en contra de los principios de un buen diseo orientado a objetos. Se trata de un tema que no es nada trivial, as que tenga siempre presente que las asignaciones pueden causar sorpresas cuando se manejan objetos

Ejercicio 2:(1) Cree una clase que contenga un valor float y utilcela para ilustrar el fenmeno de la creacin de alias. Creacin de alias en las llamadas a mtodos

El fenmeno de la creacin de alias tambin puede manifestarse cuando se pasa un objeto a un mtodo: //: operators/PassObject.java // El paso de objetos a los mtodos puede no ser // lo que cabra esperar. import static net .mindview.uti .Print. clsss Letter { char C;

) public class PassObject ( static void f{Letter y) ( y. c * ' 2';

) public static void main (String U args) { Letter x * new Letter() ; x.c = a ? print(M1: x.c: " + x.c) ; t (x) t print (H2: x.c: M + x.c);

) } / Output: 1: x.c: a 2: x.c: 2 ///:-

En muchos lenguajes de programacin, el mtodo f() haria una copia de su argumento Letter deniro del mbito del mtodo. pero aqu. una ve/ ms. lo que se est pasando es una referencia, por lo que la linea: y.c = z ' ;

loque est haciendo es cambiar el objeto que est fuera de ( ).

El fenmeno de creacin de alias y su solucin es un tema complejo de! que se trata en uno de los suplementos en linea disponibles para este libro. Sin embargo, conviene que lo tenga presente desde ahora, con el fin de detectar posibles errores.

Ejercicio 3: (1 > Cree una clase que contenga un valor float y utilcela para ilustrar el fenmeno de la creacin de alias

durante las llamadas a mtodos. Operadores matemticos

Los operadores matemticos bsicos son iguales a los que hay disponibles en la mayora de los lenguajes de programacin: suma (+), resta (-), divisin (/). multiplicacin (*) y mdulo (%. que genera el resto de una divisin entera). La divisin entera trunca en lugar de redondear el resultado.

Java tambin utiliza la notacin abreviada de C/C-*-+ que realiza una operacin y una asignacin al mismo tiempo. Este tipo de operacin se denota mediante un operador seguido de un signo de igual, y es coherente con todos los operadores del lenguaje (all donde tenga sentido). Por ejemplo, para sumar 4 a la variable x y asignar el resultado a x. utilice: x += 4

Este ejemplo muestra el uso de los operadores matemticos: //: operators/MathGps.]ava // Ilustra los operadores matemticos. import java.til.*; import static net.mindview.til.Print; public class MathOps ( public static void main(Strng [3 args) { // Crea un generador de numeres aleatorios con una cierta semilla: Random rand = new Random (47),* int i, j. k; // Elegir valor entre 1 y 100c j = rand.nextInt <10 0 i 4 1; printi"j :- j);

k = rand, next Inc (100) 4 1; print("k : " k) ,* i * j * p r i n t k : " * i) ; i - j print{"j - k : " + ); i k / -jt print ("k / j : " + i) ; I * k . j; print("k j : -t i); 1-k%j/ print("k % j r " 4 i) ; j *= imprint %= k : M j); // Pruebas con nmeros en coma flotante: float a, v, w; // Se aplica tambin a los de doble precisin v rand.nextFloatO ; print("V : M 4- v) ; w - rand.nextFloat(); print ("w : " * w) / u V w; print<"v w t " + ai); u = v - w; print (v - w : " 4 u) f u = v * wf* print (wv w : M u) j u = v / w; print(Hv / w i " * u) j // Lo siguiente tambin funciona para char, // byte, short, int, long y double: u V; print Cu v : " + u) ; u -= V; print("u -= v : n 4 u) ; u * v; print ("u ** v : h -f u); u / v; print ("u /* v : " * u) ; ) ) /* Output: j : 59 k : 56 j f k : 115 j - k : 3 k / j : 0 k * j : 3304 k % j : 56 j %k : 3
V

: 0.5209454 0.0534122

v + w : 0.5843576 V - W : 0.47753322 * w : 0.028358962v / w : 9.940527 u *= u -oV : 9.940527 u *= v : 5.2778773

10.471473

u / V : 9.940527

*///:-

Para generar nmeros, el programa crea en primer lugar un objeto Random Si se crea un objeto Random sin ningn argumento, Java usa la hora actual como semilla para el generador de nmeros aleatorios, y esto generara una salida diferente en cada ejecucin del programa. Sin embargo, en los ejemplos del libro, es importante que la salida que se muestra al final de los ejemplos sea lo ms coherente posible, para poder verificarla con herramientas externas. Proporcionando una semilla (un valor de inicializacin para el generador de nmeros aleatorios que siempre genera la misma secuencia para una determinada semilla) al crear el objeto Random, se generarn siempre los mismos nmeros aleatorios en cada ejecucin del programa, por lo que la salida se podr verificar. 11 Para generar una salida ms variada, pruebe a eliminar la semilla en los ejemplos del libro

El programa genera varios nmeros aleatorios de distintos tipos con el objeto Random simplemente invocando los mtodos nevtlii ) y nextFloat( ) (tambin se pueden invocar nextl.ong( ) o nextDoubIc( )). F.I argumento de nextlnt( ) establece la cota superior para el nmero generado. La cota superior es cero, lo cual no resulta deseable debido a la posibilidad de una divisin por cero, por lo que sumamos uno al resultado.

Ejercicio 4:
11

(2) Escriba un programa que calcule la velocidad utilizando una distancia

F.I numero 47 se utilizaba como nmero mgico" en una universidad en la que estudi. y desde entonce* lo utilizo.

constante y un tiempo constante. Operadores unarios ms y menos

El menos unario (-) y el ms unario (+) son los mismos operadores que la suma y la resta binarias. El compilador deduce cul es el uso que se le quiere dar al operador a partir de la forma en que est escrita la expresin. Por ejemplo, la instruccin: x * -a;

tiene un significado obvio. El compilador tambin podra deducir el uso correcto en:

x - a * -b;

pero esto podra ser algo confuso para el lector, por lo que a veces resulta ms claro escribir: x = a * (-b) i

El menos unario invierte el signo de los datos. El ms unario proporciona una simetra con respecto al menos unario. aunque no tiene ningn efecto. Autoincremento y autodecremento

Java, como C. dispone de una serie de abreviaturas. Esas abreviaturas pueden hacer que resulte mucho ms fcil escribir el cdigo: en cuanto a la lectura, pueden simplificarla o complicarla.

Dos de las abreviaturas ms utilizadas son los operadores de incremento y decremento (a menudo denominados operadores de autoincremento y autodecremento). El operador de decremento es y significa disminuir en una unidad. El operador de incremento es ++ y significa aumentar en una unidad. Por ejemplo, si a es un valor ni. la expresin ++a es equivalente a (a = a + 1) Los operadores de incremento y decremento no slo modifican la variable, sino que tambin generan el valor de la misma como resultado.

I lay dos versiones de cada tipo de operador, a menudo denominadas prefija y postfijo. Preincremento significa que el operador H- aparece antes de la variable, mientras que post-incremento significa que el operador ++ aparece detrs de la variable. De forma similar, pre-decremento quiere decir que el operador aparece antes de la variable y post-decremento significa que el operador aparece detrs de la variable. Para el pre-incremento y el pre-decremento (es decir. +-*-a o a), se realiza primero la operacin y luego se genera el valor. Para el post-incremento y el post-decremento (es decir, a++ o a), se genera primero el valor y luego se realiza la operacin. Por ejemplo: //: operators/AutoInc.java // I lustra los operadores -*- y import static net.mindview util.Print.j public class Autolnc ( public static void main (String [] args) {

Puede ver que para la forma prefija, se obtiene el valor despus de realizada la operacin, mientras que para la forma postn. se obtiene el valor antes de que la operacin se realice. Estos son los nicos operadores, adems de los de asignacin, que tienen efectos colaterales. Modifican el operando en lugar de simplemente utilizar su valor.

! } / Output: i : 1 M-i : 2 i+* : 2 i : 3 : 2 i-- : 2 i : 1 ///:-

El operador de incremento es. precisamente, una de las explicaciones del por qu del nombre C*H-, que quiere decir "un paso ms all de C'*\ En una de las primeras presentaciones realizadas acerca de Java, Bill Joy (uno de los creadores de Java), dijo que Java=C+-* (C ms ms menos menos), para sugerir que Java es C++ pero sin todas las complejidades inne

cesarias. por lo que resulta un lenguaje mucho ms simple. A medida que vaya avanzando a lo largo del libro, podra ver que muchas partes son ms simples, aunque en algunos otros aspectos Java no resulta mucho ms sencillo que C+-K Operadores relacinales

Los operadores relacinales generan un resultado de tipo tmoleun Evalan la relacin existente entre los valores de los opc- randos. Una expresin relacional produce el valor true si la relacin es cierta y false si no es cierta. Los operadores relacinales son: menor que (<). mayor que (>), menor o igual que (<=). mayor o igual que (>=). equivalente (=) y no equivalente (!*). La equivalencia y la no equivalencia funcionan con todas las primitivas, pero las otras comparaciones no funcionan con el tipo boolean. Puesto que los valores boolean slo pueden ser true <> false. las relaciones mayor que" y menor que no tienen sentido.

Comprobacin de la equivalencia de objetos

Los operadores relacinales = y t= tambin funcionan con todos los objetos, pero su significado suele confundir a los que comienzan a programar en Java. He aqu un ejemplo: //: operators/Equivalence.java public class Equivalence ( public static void mam(String[] args) { Integer ni = new Integer(47); Integer n2 * new Integer<47); System.out.printlnlnl == n2); System.out.Drintlntnl 1 n2);

) } / Output: false true

*///:

La instruccin System.out.printIn(nl = n2> imprimir el resultado de la comparacin boolcana que contiene. Parece que la salida debera ser "true y luego false. dado que ambos objetos Integer son iguales, aunque el contenido de los objetos son los mismos, las referencias no son iguales. Los operadores = \ != comparan referencias a objetos, por lo que la salida realmente es false"

y luego true" Naturalmente, este comportamiento suele sorprender al principio a los programadores.

Qu pasa si queremos comparar si el contenido de los objetos es equivalente? Entonces debemos utili/ar el mtodo especial equalsf ) disponible para todos los objetos {no para las primitivas, que funcionan adecuadamente con y !=). He aqu la forma en que se emplea: //: operators/EqualsMethod.java public class EqualsMethod ( public static void mam (String U args) { Integer ni * new Integeri47); Integer n2 = new Integer(47); System.out.printlntnl.equals{n2));

) ) / Output: true *///:-

Hl resultado es ahora el que esperbamos. Aunque, en realidad, las cosas no son tan sencillas. Si creamos nuestra propia clase, como por ejemplo: // operators/EqualsMethod2.java // equalsO predeterminado no compara los contenidos. class Valu ( Lnt i;

) public class EqualsMethod2 { public static void main(StringIJ args) ( Valu vi = new ValueO; Valu v2 = new Valu(); vi.i v2.i 100; System.out.println fvl.equalsv2));

) } / Output: false *///:-

los resultados vuelven a confundimos. El resultado es falso. Esto se debe a que el comportamiento predeterminado de equals() consiste en comparar referencias. Por tanto, a menos que sustituyamos equals() en nuestra nueva clase, no obtendremos el comportamiento deseado. Lamentablemente, no vamos a aprender a sustituir unos mtodos por otros hasta el captulo dedicado a la Reutilizacin de clases, y no veremos cul es la forma adecuada de definir equals( ) hasta el Capitulo 17, Anlisis detallado de los contenedores. pero mientras tanto tener en cuenta el comportamiento de equals( ) nos puede ahorrar algunos quebraderos de cabeza.

La mayora de las clases de biblioteca Java implementan equals( ) de modo que compare el contenido de los objetos, en lugar de sus referencias.

Ejercicio 5: (2) Cree una clase denominada Dog (perro) que contenga dos objetos String ame (nombre) y says

(ladrido). En main( ). cree dos objetos perro con los nombres spot" (que ladre diciendo RutY!") y scruffy" (que ladre diciendo. Wurf!"). Despus, muestre sus nombres y el sonido que hacen al ladrar.

Ejercicio 6: (3) Continuando con el Ejercicio 5, crce una nueva referencia Dog y asignela al objeto de nombre spot

Realice una comparacin utilizando = y equa!s( ) para todas las referencias. Operadores lgicos

Cada uno de los operadores lgicos AND (&&). OR (||) y NOT (!) produce un valor boolean igual a true o false basndose en la relacin lgica de sus argumentos. Este ejemplo utiliza los operadores relacinales y lgicos: //: operators/Bool.java // Operadores relacinales y lgicos, import java.util; import static net.mindview.util.Print.*;

public class Bool ( public static void main(String[] args) ( Random rand = new Random!4 7); ir*t i = rand.nextInt(100); int j = rand.nextInt (100) ; print ("i = " -* i] ; print ("j = " 4 j) ;

*{(!'< 10.) || (j < 10))

) ) /* Output t X r- 58 j = 55 i > j is true i < j is false i >= j is true i <= j is false i == 3 is false i 1* j is true U < 10) 6c* (j < 10) is li < 1 0 1 || (j < 10) is

false false

///:-

Slo podemos aplicar AND, OR o NOT a valores de tipo boolean. No podemos emplear un valor que no sea boolean como si fuera un valor booleano en una expresin lgica como a

que sucede en C y el mediante

diferencia de C++. Puede ejemplolos intentos fallidos de realizar esto, marcas decomentarios *//! (esta sintaxis comentarios

desactivados de

permite la eliminacin automtica de comentarios para facilitar las pruebas). Sin embargo, las expresiones subsiguientes generan valores de tipo boolean utilizando comparaciones relacinales y a continuacin realizan operaciones lgicas con los resultados.

Observe que un valor boolean se convierte automticamente a una forma de tipo texto apropiada cuando se les usa en lugares donde lo que se espera es un valor de tipo String

Puede reemplazar la definicin de valores ni en el programa anterior por cualquier otro tipo de dato primitivo excepto boolcan. Sin embargo, teniendo en cuenta que la comparacin de nmeros en coma flotante es muy estricta, un nmero que difiera de cualquier otro, aunque que sea en un valor pequesimo seguir siendo distinto. Asimismo, cualquier nmero situado por encima de cero, aunque sea pequesimo, seguir siendo distinto de cero.

Ejercicio 7:(3)Escriba

unprograma

que

simule el proceso de lanzar una moneda al aire.

Cortocircuitos

Al tratar con operadores lgicos, nos encontramos con un fenmeno denominado cortocircuito. Esto quiere decir que la expresin se evaluar nicamente hasta que la veracidad o la falsedad de la expresin completa pueda ser determinada de forma no ambigua. Como resultado, puede ser que las ltimas partes de una expresin lgica no lleguen a evaluarse. He aqu un ejemplo que ilustra este fenmeno de cortocircuito. //: operators/ShortCircuit. java // Ilustra el comportamiento de cortocircuito // al evaluar los operadores lgicos, import stacic ne: .ramview.util. Pnnc . * ; public class ShortCircuit ( return val <2;

i static boolean test3(int val) { print("test3 (" * val + ")*); print "result: "(val < 3)); return val < 3;

i public static voad mainString[I args) { boolean b = testl(0) && test2(2) uu test3l2); print("expresaion is *' * b) ;

l } /* Output: testl(0) result: true test2(2) result: false expreasion is false *///:-

Cada una de las comprobaciones realiza una comparacin con el argumento y devuelve true o false Tambin imprime la informacin necesaria para que veamos que est siendo invocada. Las pruebas se utilizan en la expresin: testl(0) && test2(2) && test3(2)

Lo natural sera pensar que las tres pruebas llegan a ejecutarse, pero la salida muestra que no es as. La primera de las pruebas produce un resultado true. por lo que contina con la evaluacin de la expresin. Sin embargo, la segunda prueba produce un resultado false. Dado que esto quiere decir que la expresin completa debe ser false. por qu continuar con la evaluacin del resto de la expresin? Esa evaluacin podra consumir una cantidad considerable de recursos. La razn de que se produzca este tipo de cortocircuito es, de hecho, que podemos mejorar la velocidad del programa si no es necesario evaluar todas las partes de una expresin lgica. Literales

Normalmente, cuando insertamos un valor literal en un programa, el compilador sabe exactamente qu tipo asignarle. En ocasiones, sin embargo, puede que ese tipo sea ambiguo. Cuando esto sucede, hay que guiar al compilador aadiendo cierta informacin adicional en la forma de caracteres asociados con el valor literal. El cdigo siguiente muestra estos caracteres: //: operators/Literals.java import static net.mindview.til.Print . * ; puJblic class Literals ( public static void main( String [] args) { int il = 0x2f; //

Hexadecimal (minscula) print("il: " * Integer.toBinaryString(il)) ; int i2 = 0X2 F; // Hexadecimal (mayscula) pnnt("i2: M Integer.toBinaryStringti2)); int i3 = 0177; // Octal (cero inicial) printfi3: " * Integer.toBinaryString(i3))/ char c = Oxffff; // mximo valor hex para char print fe: " + Integer. toBinaryString (c) ) ; byte b = 0x7f; // mximo valor hex para byte printfb: w + Integer.toBinaryString(b)) ; short s = 0x7fff; // mximo valor hex para short print fs: " Integer. toBinaryString(s)) ; long ni = 20QL; // sufijo lona long n2 * 2001; // sufijo long (pero puede ser confuso) long n3 = 200; float fl = 1; float f2 1F; // sufijo float float f3 = lf; ff sufijo float double di = Id; // sufijo double double d2 = ID; // sufijo double // <Hex y Octal tambin funcionan con long)

) } /* Output: il: 101111 2: 101111 13: 1111111 c: 1111111111111U1 b: 1111111 S : 111111111111111

*///:-

Un carcter situado al final de un valor literal permite establecer su tipo La L mayscula o minscula significa Ion} (sin embargo, utilizar una I minscula es confuso, porque puede parecerse al nmero uno). Una F mayscula o minscula significa float Una D mayscula o minscula significa double.

Los valores hexadecimales (base 16), que funcionan con todos los tipos de datos enteros, se denotan mediante el prefijo Ox

o 0X seguido de 0-9 o a-f en mayscula o minscula. Si se intenta inicializar una variable con un valor mayor que el mximo que puede contener (independientemente de la forma numrica del valor), el compilador dar un mensaje de error. Observe, en el cdigo anterior, los valores hexadecimales mximos permitidos para char. byte y short. Si nos excedemos de stos, el compilador transformar automticamente el valor a int y nos dir que necesitamos una proyeccin hacia abajo para la asignacin (definiremos las proyecciones posteriormente en el capitulo). De esta forma, sabremos que nos hemos pasado del lmite permitido.

Los valores octales (base 8) se denotan incluyendo un cero inicial en el nmero y utilizando slo los dgitos 0-7.

No existe ninguna representacin literal para los nmeros binarios en C, C++ o Java. Sin embargo, a la hora de trabajar con notacin hexadecimal y octal. a veces resulta til mostrar la forma binaria de los resultados. Podemos hacer esto fcilmente con los mtodos static toBinaryString( ) de las clases Integer y Long. Observe que. cuando se pasan tipos ms pequeos a Integer.toBinaryString ). el tipo se convierte automticamente a int

Ejercicio 8: long. Utilice

(2) Demuestre que las notaciones hexadecintal y ocia! funcionan con los valores

Long.toBinarvString( ) para mostrar los resultados. Notacin exponencial

Los exponentes utilizan una notacin que a mi personalmente me resulta extraa: //: operators/Exponents.java // Me* significa 10 elevado a". public class Exponen!:5 ( public static void main(Strlng[] args) ( // *e' en mayscula o minscula funcionan igual: float expFloat = 1.39e-43f? expFloat * 1.39E-43f; System.out.println(expFloatJ; double expDouble * 47e4 7d; // ' d es opcional double expDoubie2 = 47e47; // automticamente double System.out.println(expDouble)

) ) /* Outpuc: 1.39E-43 4.7E48

///;-

En el campo de las ciencias y de la ingeniera, *e hace referencia a la base de los logaritmos naturales, que es aproximadamente 2.718 (en Java hay disponible un valor double ms preciso, que es Math.E). Esto se usa en expresiones de exponen- ciacin. como por ejemplo 1.39 x e4-\ que significa 1.39 x 2.71843. Sin embargo, cuando se invent el lenguaje de programacin FORTRAN, decidieron que e significara diez elevado a, lo cual es una decisin extraa, ya que FORTRAN fue diseado para campos de la ciencia y de la ingeniera, asi que cabria esperar que sus diseadores tendran en cuenta lo confuso de introducir esa ambigedad 12. En cualquier caso, esta costumbre fue tambin introducida en C, O * y ahora en Java. Por tanto, si el lector est habituado a pensar en e como en la base de los logaritmos naturales, tendr que hacer una tniduccin mental cuando vea una expresin como 1.39 e-43f en Java; ya que quiere decir 1.39 x 10"

Observe que no ca necesario utilizar el carcter sufijo cuando el compilador puede deducir el tipo apropiado. Con: long n3 * 200;

John Kirkhum escribe. Comenc a escribir programas informticos en 1962 en FORTRAN II en mi IBM 1620 Por aquel entonces y a lo largo de las dcadas te 1960 y 197. FORTRAN era un lenguaje donde todo se escriba en maysculas Probablemente, la razn era que muchos de los dispositivos de entrada eran anticuas unidades de teletipo que utilizaban el cdigo Baudot de cinco bits que no dispona de mmusculas. La ~E" en la notacin exponencial era tambin mayscula y no se conlundia nunca con la base de los logaritmos naturales "e" que siempre se escribe cu minscula La **E simplemente quera decir exponencial, que era la base para el sistema de numeracin que *c estaba utilizando, que normalmente era 10 En aquella poca, los programadores tambin empleaban los nmeros ocales. Aunque nunca vi que nadie lo utilizara, si yo hubiera visto un nmero octal en notacin exponencial. habra considerado que estaba en base 8. l.a pnmera vez que vi tin exponencial uiilizando una fue a finales de la dcada de 1970 y tambin a mi me pareci contuso; el problema surgi cuando empezaron a utilizarse mmscula> en FOKTAN. no al principio. De hecho, disponame^ de funciones que podan usarse cuando <c quisiera emplear la base de los logaritmos naurnles. pero todas esas funciones se escriban en maysculas.
12

no existe ninguna ambigedad, por lo que una L despus del 200 seria superiluo. Sin embargo, con: float f4 = le-43f; // 10 elevado a

el compilador normalmente considera los nmeros exponenciales como de tipo double por lo que sin la f final, nos daria un error en el que nos informara de que hay que usar una proyeccin para convertir el valor double a float

Ejercicio 9: (1) Visualice los nmeros ms grande y ms pequeo que se pueden representar con la notacin exponen

cial en el tipo lloat y en el tipo double Operadores bit a bit

t.os operadores bit a bit permiten manipular bits individuales en un tipo de datos entero primitivo. Para generar el resultado. los operadores bii a bit realizan operaciones de lgebra booleana con los bits correspondientes de los dos argumentos.

Los operadores bit a bit proceden de la orientacin a bajo nivel del lenguaje C. en el que a menudo se manipula el hardware directamente y es preciso configurar los bits de los registros hardware. Java se disert originalmente para integrarlo en codificadores para televisin, por lo que esta orientacin a bajo nivel segua teniendo sentido. Sin embargo, lo ms probable es que no utilicemos demasiado esos operadores bit a bit en nuestros programas.

Til operador bit a bit AND &) genera un uno en el bit de salida si ambos bus de entrada son iguales a uno; en caso contrario. genera un cero. El operador OR bit a bit (|) genera un uno en el hit de salida si alguno de los bits de entrada es un uno \ genera cero slo si ambos bus de emrada son cero. El operador bu a bu EXCLUSI\'fc OR o XOR (43A) genera un uno en el bit de salida si uno de los dos bits de entrada es un uno pero no ambos. El operador bit a bit NOT (^. tambin denominado operador de complemento o uno) es un operador unario. que slo admite un argumento (todos los demas operadores bit a bii son operadores binarios). El operador bit a bit NOT genera el opuesto al bit de entrada, es uno si el hit de entrada es cero y es cero si el bit de entrada es uno.

Los operadores bit a bit y los operadores lgicos utilizan los mismos caracteres, por lo que resulta til recurrir c un truco mnemnico para recordar cul es el significado correcto. Como los bits son pequeos slo se utiliza un carcter en los operadores bit a bit.

Los operadores bit a bit pueden combinarse con el signo = para unir la operacin y la asignacin: &=. |= y A= son operadores legtimos (puesto que - es un operador unario. no se puede combinar con el signo =),

El tipo boolean se traa como un valor de un nico bit, por lo que es algo distinto de los otros tipos primitivos. Se puede realizar una operacin AND. OR o XOR bit a bit. pero no se puede realizar una operacin NOT bit a bit (presumiblemente. para evitar la confusin con la operacin lgica NOT). Para los valores booleanos. los operadores bit a bit tienen el mismo efecto que los operadores lgicos, salvo porque no se aplica la regla de cortocircuito. Asimismo, las operaciones bit .1 bit con valores booleanos incluyen un operador lgico XOR que no forma parte de la lista de operadores 'lgicos*. No se pueden emplear valores booleanos en expresiones de desplazamiento, las cuales vamos a describir a continuacin,

Ejercicio 10: (3) Escriba un programa con dos valores constantes, uno en el que haya unos y ceros binarios a temados,

con un cero en el dgito menos significativo, y el segundo con un valor tambin alternado pero con un uno en el digno menos significativo (consejo: lo ms fcil es usar constantes hexadecimales para es;o). Tome estos dos valores y combnelos de todas las formas posibles utilizando los operadores bit a bit. y visualice los resultados utilizando lnte^er.toBinarvString( ). Operadores de desplazamiento

Los operadores de desplazamiento tambin sirven para manipular bits. Slo se les puede utilizar con tipos primitivos enteros. El operador de desplazamiento a la izquierda () genera como resultado el operando situado a la izquierd del operador despus de desplazarlo hacia la izquierda el nmero de bits especificado a la derecha del operador (insertando ceros en los bits de menor peso). El operador de desplazamiento a la derecha con signo () genera como resultado el operando situado a la izquierda del operador despus de desplazarlo hacia la derecha el nmero de bits especificado a la derecha del operador. El desplazamiento a la derecha con signo utiliza lo que se denomina extensin Je signo: si el valor es positivo. se insertan ceros en los bits de mayor peso; si el \ alor es negativo, se insertan unos en los bits de mayor peso. Java lia aadido Lambin un desplazamiento a la derecha sin signo >. que utiliza lo que denomina extensin con ceros: independientemente del signo, se insertan ceros

en los bits de mayor peso. Este operador no existe ni en C ni OH%

Si se desplaza un valor de tipo char. byte o short. ser convertido a int antes de que el desplazamiento tenga lugar y el resultado ser de tipo int. Slo se utilizaran los bits de menor peso del lado derecho; esto evita que se realicen desplazamientos con un nmero de posiciones superior al nmero de bits de un valor int Si se est operando con un valor Ion, se obtendr un resultado de tipo long y slo se emplearn los seis bits de menor peso del lado derecho, para asi no poder desplazar ms posiciones que el nmero de bits de un valor long.

Los desplazamientos se pueden combinar con el signo igual (= o = o >=). El Ivalor se sustituye por el Ivalor desplazado de acuerdo con lo que el rvalor marque. Existe un problema, sin embargo, con el desplazamiento a la derecha sin

signo combinado con la asignacin. Si se usa con valores de tipo byte o short, no se obtienen los resultados correctos. En lugar de ello, se transforman a int y luego se desplazan a la derecha, pero a continuacin se truncan al volver a asignar los valores a sus variables, por lo que se obtiene 1 en esos casos. El siguiente ejemplo ilustra esta situacin: //: operators/tJRShift. java II Prueba del desplazamiento a la derecha sin signo, import static net.mindview.util.Print. * ; public class URShift { public static void mam (String [] args) { int i -If print(Integer.toBinaryStri ng(il): t >>> 10; print(Integer.toBinaryStri ng ti)); long 1 -1;

print(Long.toBinaryString(l));

1 >>>* 10; print(Long.toBinaryString( 1)); short s = -1; print(Integer.toBinaryStri ng is)); s >>>* 10; print(Integer.toBinaryStri ng(s)); byte b * -1; print(Integer.toBinaryStri ng(b)); b >>>= 10; print(Integer.toBinaryStri ng(b)); b * -If print(Integer.toBinaryStri ng(b)) print(Integer.toBinaryStri ng(b>>>10));

} ) /* Output: 11111111111111111111111111111111 1111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 111111111111X11111X1111111111111 11111111111111111111111111111111 11111111111111111111111111111111 11111111.11111111111111

*///:-

En el ltimo desplazamiento, el valor resultante no se asigna de nuevo a b. sino que se imprime directamente, obtenindose el comportamiento correcto.

He aqu un ejemplo que ilustra todos los operadores para el manejo de bits: II: operators/BitManipulation.java // Uso de los operadores bit a bit. import java.util.*; import static net.mindview.util.Print; public class BitManipulation ( public static void main(String[] args) ( Random rand ** new Random (47); int i n rand, next Int O int j = rand.nextInt() ; printBinaryInt(M-1H, -1); printBinarylnt(M +1", !) ;int maxpos - 2147483647; printBinarylnt("maxpos", maxpos); int maxneg = -2147483648; printBinarylnt("maxneg", maxneg); printBinarylnt("i", i) ; printBinarylnt("-i", -i); printBinarylnt("i", -); printBinarylnt("j*, j); printBinarylnt C i & j" , i & i); printBinarylnt (" (-i) >>> 5", (-i} > 5); long 1 = rand.nextLona(); long m * rand. nextLong () ; printBinaryLong M-IL", -IL); printBinaryLong ("flL, flL) ; long 11 = 9223372036854775807L; printBinaryLong(HmaxposH, 11); long lln = -922337203685477580BL; printBinaryLong("maxneg". 1ln); printBinaryLongHl", 1); printBinaryLong("-1". -1); printBinaryLong(M-1". -1); printBinaryLong"m", m) ; pr me Bina ry Long ("1 & mM, I & m) ; printBinaryLong("1 | m", 1 | m) ; printBinaryLong ("1 ra*f IA m) ,* printBinaryLong('l << 5", 1 5); printBinaryLong(1 5", 1 >> 5); printBinaryLong("{-1) >> 5 ", (-1)>>5); printBinaryLong ("1 > 5", 1 >>> 5); printBinaryLong(M(1) >>> 5", (-1)>>>5);

} atacic void printBinarylnt (String s, int i) { print {s +- ", int: " + i ", binary:\n " Integer.toBinaryString(i));

) static void printBinaryLong(String s, long 1) { print(s + ", long: " 1 + binary:\n " Long.toBinaryString(1));

} } / * Output: -1, int: -1, bmary: 11111111111111111111111111111111 1, int: 1, binary: 1 maxpos, int: 2147483647, binary: 1111111111111111111111111111111 maxneg, int: -2147483648, binary: 10000000000000000000000000000000 i, int: -1172028779, binary: 10111010001001000100001010010101 -i, int: 1172028778, binary: 100010111011011101111010110101 0-i. int: 1172028?79, binary: 100010111011011101111 0101101011 j, int: 1717241110, binary: 110011001011011000001 0100010110 i * j. mt: 570425364, binaryj 10001000000000000000000 0010100 i I j, int: -25213033, binaryi 111111100111111.10100 011110010111 i A j, int: -595638397, binary: 110111000111111101000 11110000011 i 5, int; 11497Q4736, binary: 1000100100010000101001 010100000 i 5, int: -36625900, binary:

111111011101000100100 01000010100 t~A) 5, int: 36625899, binary: 10001011101101110111101011 i >> 5, int: 97591828, binary: 101110100010010001000010100 (iI > 5, int: 36625899, binary: 10001011101101110111101011

*///:-

Los dos mtodos del final, print Binary lnt() y print Binary Long(). toman un valor Int o long. respectivamente, y lo imprimen en formato binario junto con una cadena de caracteres descriptiva. Adems de demostrar el efecto de iodos los operadores bit a bit para valores int y long. este ejemplo tambin muestra los valores mnimo, mximo, +1 y -1 para int y long para que vea el aspecto que tienen. Observe que el bit ms alto representa el signo: 0 significa positivo y 1 significa negativo. En el ejemplo se muestra la salida de la parte correspondiente a los valores int.

La representacin binaria de los nmeros se denomina complemento o dos con .signo.

Ejercicio 11: (3) Comience con un nmero que tenga un uno binario en la posicin ms significativa (consejo: utilice

una constante hexadecimal). Emplee el operador de desplazamiento a la derecha con signo, desplace el valor a travs de todas sus posiciones binarias, mostrando cada vez el resultado con Integer.toBinaryStringt )

Ejercicio 12: (3) Comience con un nmero cuyos dgitos binarios sean todos guales a uno. A continuacin desplcelo

a la izquierda y utilice el operador de desplazamiento a la derecha sin signo para desplazarlo a travs de todas sus posiciones binarias, visualizando los resultados con Integer.toBinaryStrng( ).

Ejercicio 13: (I) Escnba un mtodo que muestre valores char en formato binario. Ejectelo utilizando varios caracte

res diferentes.

Operador ternario if-else

El operador ternario, tambin llamado operador condicional resulta inusual porque tiene tres operandos. Realmente se trata de un operador, porque genera un valor a diferencia de la instruccin if-else ordinaria, que veremos en la siguiente seccin del capitulo. La expresin tiene la forma: exp-booleana ? valoro j valorl

Si exp-booleana se evala como true, se evala vabrO y el resultado ser el valor generado por el operador. Si exp- booleana es false. se evala valorl y su resultado pasar a ser el valor generado por el operador.

Por supuesto, podra utilizarse una instruccin If-else ordinaria (que se describe ms adelante), pero el operador temario es mucho ms compacto. Aunque C (donde se origin este operador) se enorgullece de ser un lenguaje compacto, y el operador temario puede que se haya introducido en parte por razones de eficiencia, conviene tener cuidado a la hora de emplearlo de forma cotidiana, ya que el cdigo resultante puede llegar a ser poco legible.

El operador condicional es diferente de if-else porque genera un valor. He aqu un ejemplo donde se comparan ambas estructuras: //: operators/TernarylfElse.jav

a lnport static net.mndview.til.Print.*? pubic ciass TernaryIE1se ( static int ternarylint i) { retum i < 10 ? i 100 : i 10;

) static int scandardlfSise(int i) { ifCi < 10) return i 100; else return i 10;

) public static void main(String[] args' ( prmt (ternary O) ) ; print(ternary(10) ) ; print(standardlfElse(9)) ; print(standardlfElse 10));

) ) / Gutput: 900 100 900

100

*///r-

Puede ver que el cdigo de ternarv( ) es ms compacto de lo que sera si no dispusiramos det operador temano: la versin sm operador lemario se encuentra en standardlflllse( ) Sin embargo. standard lfEIse< ) es ms fcil de comprender y adems exige escribir muchos caracteres ms. Asi que asegrese de ponderar bien las razones a la hora de elegir el operador temario; normalmente, puede convenir utilizarlo cuando se quiera configurar una variable con uno de dos valores posibles. Operadores + y += para String

Existe un uso especial de un operador en Java: los operadores + y += pueden usarse para concatenar cadenas, como ya hemos visto. Parece un uso bastante natura! de estos operadores, an cuando no encaje demasiado bien con la forma tradicional en que dichos operadores se emplean.

Esta capacidad le pareci adecuada a los diseadores de C*H*. por lo que se aadi a C-H- un mecanismo de sobreetn-ga de operadores para que los programadores C pudieran aadir nuevos significados casi a cualquier operador. Lamentablemente. la sobrecarga de operadores combinada con alguna de las otras restricciones de C-H-. resulta una caracterstica excesivamente complicada para que los programadores la incluyan en el diseo de sus clases Aunque la sobrecarga de operadores habra sido mucho ms fcil de implementar en Java de lo que lo era en C++ (como se ha demostrado

en el lenguaje C#. que si que dispone de un sencillo mecanismo de sobrecarga de operadores), se consideraba que esta caracterstica segua siendo demasiado compleja, por lo que los programadores Java no pueden implementar sus propios operadores sobrecargados, a diferencia de los programadores de C+4- y C#.

El uso de los operadores para valores String presenta ciertos comportamientos interesantes. Si una expresin comienza con un valor String. todos los operandos que siguen tambin tendrn que ser cadenas de caracteres (recuerde que el compilador transforma automticamente a String toda secuencia de caracteres encerrada entre comillas dobles). //: operators/StrmgOperators. java mport static net.mindview.til.Print; public class StringOperators ( public static void mam (String [1 argsl { int x = 0, y = 1, 2 = 2; String s * Mx, y, z M; print (s + x y +*); printx s) i // Convierte x a String s M (sumtned) " ; // Operador de concatenacin printfs + (x + y + z)); printi"" x); // Abreviatura de Integer.toStrinoO

i ) f* Outpuc: x, y, s 012
0

x, y, z

x, y, 2 (summed) * 3 0

*///:-

Observe que la salida de la primera instruccin de impresin es *ol2en lugar de slo 3, que es lo que obtendra si se estuvieran sumando los valores enteros. Esto es porque el compilador Java convierte x. y y / a su representacin String y concatena esas cadenas de caracteres, en lugar de efectuar primero la suma. La segunda instruccin de impresin conviene la variable inicial a String. por lo que la conversin a cadena no depende de qu es lo que haya primero. Por ltimo, podemos ver el uso del operador += para aadir una cadena de caracteres a s. y el uso de parntesis para controlar el orden de evaluacin de la expresin, de modo que los valores enteros se sumen realmente antes de la visualizacin.

Observe el ltimo ejemplo de main( ): en ocasiones, se encontrar en los programas un valor String vaco seguido de + y una primitiva, como forma de realizar la conversin sin necesidad de invocar el mtodo explcito ms engorroso. lnteger.toString ). en este caso). Errores comunes a la hora de utilizar operadores

Uno de los errores que se pueden producir a la hora de emplear operadores es el de tratar de no incluir

los parntesis cuando no se est del todo seguro acerca de la forma que se evaluar una expresin. Esto, que vale para muchos lenguajes tambin es cierto para Java.

Un error extremadamente comn en C y C*H sera el siguiente:

whilelx * y) ( // . . . .

El programador estaba intentando, claramente, comprobar la equivalencia (=) en lugar de hacer una asignacin. En C y C++ el resultado de esta asignacin ser siempre true si y es distinto de cero, por lo que probablemente se produzca un bucle infinito. En Java, el resultado de esta expresin no es de tipo boolcan. pero el compilador espera un valor hoolean y no realizar ninguna conversin a partir de un valor int, por lo que dar un error en tiempo de compilacin y detectar el problema antes de que ni siquiera intentemos ejecutar el programa. Por tanto, este error nunca puede producirse en Java (la nica posibilidad de que no se tenga un error de tiempo de compilacin, es cuando ley son de tipo hoolean. en cuyo caso x = y es una expresin legal, aunque en el ejemplo anterior probablemente su uso se deba a un error).

Un problema similar en C y C-H- consiste en utilizar los operadores bit a bii AND y OR en lugar de las versiones lgicas. Los operadores bit a bit AND y OR utilizan uno de los caracteres (& o |) mientras que los operadores lgicos AND y OR utilizan dos (&& y ||). Al igual que con = y =, resulta fcil confundirse V escribir slo uno de los caracteres en lugar de dos. En Java, el compilador vuelve a evitar este tipo de error, porque no permite emplear un determinado tipo de datos en un lugar donde no sea correcto hacerlo Operadores de proyeccin

La palabra proyeccin (cast) hace referencia a la conversin explcita de datos de un tipo a otro Java cambiar automticamente un tipo de datos a otro cada vez que sea apropiado. Por ejemplo, si se asigna un valor entero a una variable de coma flotante, el compilador convertir automticamente el valor int a float. El mecanismo de conversin nos permite realizar esta conversin de manera explcita, o incluso forzarla en situaciones donde normalmente no tendra lugar.

Para realizar una proyeccin, coloque el tipo de datos deseado entre parntesis a la izquierda del valor que haya que convertir. como en el siguiente ejemplo: //: operatora/Casting.java public class Casting [ public static void main(String[] args) ( int i * 200; long Ing =* (long)u lng = i; // "Ensanchamiento," por lo que no se requiere conversin long lng2 * (long)200; lng2 * 200; // Una "conversin de estrechamiento ": i = (int)lng2; // Proyeccin recruerida

} ///:-

Como podemos ver, resulla posible aplicar una proyeccin de tipo tanto a los valores numricos como a las variables. Observe que se pueden tambin introducir proyecciones superfluas. por ejemplo, el compilador promocionar automticamente un \alor int a long cuando sea necesario. Sin embargo, podemos utilizar esas proyecciones superfluas con el fin de resaltar la operacin o de clarificar el cdigo. En otras situaciones, puede que la proyeccin de tipo sea esencial para que el cdigo llegue a compilarse.

En C y C-*-+f las operaciones de proyeccin de tipos pueden provocar algunos dolores de cabeza. En Java, la proyeccin de tipos resulta siempre segura, con la excepcin de que. cuando se realiza una de las denominadas conversiones Je estrecha- miento (es decir, cuando se pasa de un tipo de datos que puede albergar ms informacin a otro que no permite albergar tanta), se corre el riesgo de perder informacin. En estos casos, el compilador nos obliga a emplear una proyeccin, como dicindonos: Esta conversin puede ser peligrosa, si quieres que lo haga de todos modos, haz que esa proyeccin sea explcita*'. Con una conversin Je ensanchamiento, no hace falta una proyeccin explcita, porque el nuevo tipo permitir albergar con creces la informacin del tipo anterior, de modo que nunca se puede perder informacin.

Java permite proyectar cualquier tipo primitivo a cualquier otro, excepto en el caso de honlean. que no permite efectuar ningn lipo de proyeccin. Los tipos de clase tampoco permiten efectuar

proyecciones: para convenir uno de estos tipos en otro, deben existir mtodos especiales (posteriormente, veremos que tos objetos pueden proyectarse dentro de una familia de tipos; un Olmo puede proyectarse sobre un Arbol v viceversa, pero no sobre un tipo extemo como pueda ser Roca). Truncamiento y redondeo

Cuando se realizan conversiones de estrechamiento, es necesario prestar atencin a los problemas de truncamiento y redondeo. Por ejemplo, si efectuamos una proyeccin de un valor de coma flotante sobre un valor entero, qu es lo que hara Java? Por ejemplo, si tenemos el valor 29.7 y lo proyectamos sobre un inl. el valor resultante ser 30 o 29? I \ respuesta a esta pregunta puede verse en el siguiente ejemplo: //*. operators/CastinaNumbers.java // Qu ocurre cuando se proyecta un valor float // o double sobre un valor entero? import static net.mindview.til.Print.; public class CastmgNumbers ( public static void main(String[1 args) ( double above 0.7, below = 0.4; float fabove * 0.7f, fbelow * 0.4f; print("(int)above: " + (int)above); print("(intJbelow: " * {int)below); print(H(int)fabove: " * (int)fabove); print("(int)fbelow: M (int)fbelow);

i ) /* Output: (int)abov e: 0 (int)belo w: 0

(int)fabo ve: 0 (int)fbel ow: 0 ///:-

Asi que la respuesta es que al efectuar la proyeccin de float o double a un valor entero, siempre se trunca el correspondiente nmero. Si quisiramos que el resultado se redondeara habra que utilizar los mtodos round() de java.lang.Math //: operators/RoundingNumbers. java // Redondeo de valores float y double. import static net .mindview.til .Print. ; public class RoundingNumbers ( public static void main(String[J aras) { double above * 0.7, below = 0.4; float fabove = 0.7fr fbelow = o.4f; print i "Math. round i above l : " Math.round(above!}; printi"Math.round(below): " Math.round(below)); printl"Math.round(fabovei: n + Math.round(fabove)); print'''Math. round (fbelow) : " Math.round(fbelow));

} | / Output: Math.round(abov e): 1 Math.round(belo wl: 0 Math.round(fabo ve): 1 Math.roundlfbel owJ : 0

///:-

Puesto que round( ) es pane de java.lang. no hace falta ninguna instruccin adicional de importacin para utilizarlo. Promocin

Cuando comience a programar en Java, descubrir que si hace operaciones matemticas o bit a bit con tipos de dalos primitivos ms pequeos que int (es decir, cliar. Inte o short). dichos valores sern promocionados a int antes de realizar las operaciones, y el valor resultante ser de tipo int. Por tanto, si se quiere asignar el resultado de nuevo al tipo ms pequeo, es necesario emplear una proyeccin (V, como estamos realizando una asignacin a un tipo de menor tamao, perderemos informacin). En general, el tipo de datos de mayor tamao dentro de una expresin es el que determina el tamao del resultado de esa expresin, si se multiplica un valor float por otro double, el resultado ser double: si se suman un valor nt y uno long. el resultado ser long. Java no tiene operador sizeof

En C y C++, el operador sfzeof( > nos dice el nmero de bvtes asignado a un elemento de datos. La razn ms importante para el uso de sizeoff ) en C y C-H- es la portabilidad. Los diferentes tipos de datos pueden tener diferentes tamaos en distintas mquinas, por lo que el programador debe averiguar el tamao de esos tipos a la hora de realizar operaciones que sean sensibles al tamao. Por ejemplo, una computadora puede almacenar los enteros en 32 bits, mientras que otras podran almacenarlos en 16 bits Los programas podran, as. almacenar valores de mayor tamao en variables de tipo entero en la primera mquina. Como puede imaginarse, la portabilidad es un verdadero quebradero de cabeza para los programa- dores de C y C+-K

Java no necesita un operador sizeof( ) para este propsito, porque todos los tipos de datos tienen el mismo tamao en todas las mquinas. No es necesario que tengamos en cuenta la portabilidad en este nivel, ya que esa portabilidad forma parte del propio diseo del lenguaje. Compedio de operadores

El siguiente ejemplo muestra qu tipos de datos primitivos pueden utilizarse con determinados operadores concretos. Bsicamente, se irata del mismo ejemplo repetido una y otra vez pero empleando diferentes tipos de datos primitivos. El archivo se compilar sin enores porque las lineas que los incluyen estn desactivas mediante comentarios de tipo //!. //: operators/AHOps. java // Comprueba todos los operadores con todos los tipos de datos primitivo

s// para mostrar cules son aceptables por el compilador Java. 3 Operadores public class AilOps ( // Para aceptar los resultados de un test booleano: void f(boolean b) () void boolTest(boolean x. boolean y) ( / / Operadores aritmticos:

fix == y); f(x i y); f(ly); x = x ti y; X * x || y; // Asignacin compuesta: //! X y; //! x -= y; //I x *= y; //l x /= y; //! x %= y; / / [ X = 1; //l x 1; //l x >= 1; x &= y; X y; x I y; // Proyeccin: / / [ char c = (char)x;

/ / [ byte b = (bytejx; //1 short s * (short)x; //i int i s (int)x; f f I long 1 * (long)x; //1 float f = (float)x; //i double d = (double)x;

x Piensa en Java

void charTest(char x, char y) // Operadores aritmticos: x = (char) (x * y) ; x (char) (x / y) ;

X++;

X-- ; x - (char)y; x = (char)-y; // Relacinales lgicos: f (x > y) ,* f ix >= y) ; fix < y); f (x <= y) / f(x == y) ; fix !- y) ,* //! f(iX> ? //! f (x fit& y) ; //! f (x H y); // Operadores bit a bit: x* (char)-y; x = (char) (x & y) ; x = (char)(x | y); x = (char)(x A y) ; x = 1) ; X * (char)(x << (char)(x >> y

x Piensa en Java

1) ; x = 1); // Asignaci n compuest a: x += y; x -= y; x *= y; x /= y; x %* y; (char)(x >>>

X = 1; x >>o 1; x >= l; x &= y f x y; x |= y; // Proyeccin: //I boolean (boolean)x byte b = (byte)x; shore a = (short)x; int i * lint)xr* long 1 = 'long)x; float f * (float)x; double d = (double)x; bl *

> void byteTest(byte x, byte y) // Operadores aritmticos: x = (byte)

x Piensa en Java

<x* y) ; x y) x++; x--;

=(byte)(x / ;

x = (byte)+ y; x (byte)- yr

3 Operadores 186

// Relacinales y lgicos: f (x > y) j f (x >= y); f (x < y); f tx <= y); (x *= y) ,* fix fa y) ; //! f f 1 x) ; f / \ l (x y} r //i f(x || y),- // Operadores bit a bit: x = (byte)-y x = (byte) (x & y) ; x * (byte)(x | y) ; x (byte) (x A y) ; x (byte) (x 1); x = (byte) (x 1); x * (byte) (x > 1) ; // Asign acin compu esta: x -f y; x y; x *= y; x / y; x %= y; x = 1;

X >>=1;

3 Operadores 187

X >>> 1; x fca y; X A= y; x |= y; // Proyeccin: //! boolean (boolean)x; bl =

char c *= (char)x; short s = (short)x; int i = (int)x/ long 1 = (long)x; float f = (float)x; double d * (double)x; ) xi x * (short)+y; x = lshort)-y; // Relacinales y lgicos: f(x > y); <x >= y); f (x < y) ; f (x <= y); f(x == y) ; f (x U y); f f I fdx); //I f (x Scc y) ; //i fix II y);

x Piensa en Java

// Operadores bit a bit:

x * (short)(x >> 1); x = (short)(x >>> L) ; t t Asignacin compuesta: x y; x -* yj x *= y; x /- y ; x I y; x <<= 1;

X * 1/

X >>>= 1; x y; x y;

x h y;

x Piensa en Java

// Proyeccin:

//I boolean bl = (boolean):

char c - (char)x;

byte b * (byte)X/

int i * (int)x;

x Piensa en Java

long 1 = (long)x;

float f = (float)x;

double d = (double)x;

void intTest(int x, int y) ( // Operadores aritmticos: x * x y; x = x / y; x x % y; x = x + y; x * x - y;

x Piensa en Java

X+fJ X--J x * y; x = -y;

// Relacinales y lgicos: f (x > y) ,* t (x >- y); f (x < y) j f (x c y); t tx y); f (x 1= y);

U\ f ( ! x ) ;

/ / 1 f (x U U y) ;

//t f(x || y);

x Piensa en Java

// Operadores bit a bit: x * -y; x * x & y; x X I y; x = x * y; x * X 1;

X = X >> 1 ;

X = X >>> 1;

3 Operadores 193 // Asignacin compuesta: x y ; x -= y; x ** y;

X /* y; X %= y; x * 1 ,*

X = 1;

X >>>= 1; x &= y; x *= y; X I y: // Proyeccin: //! boolean bl * (boolean)x; char c = (charix; byte b = (byte)x; short s = (short)x; long 1 = (longjx; float f = (float)x; double d = (double)x;

3 Operadores 194 ) void longTest(long x, long y) { // Operadores aritmticos: x x y; X = X / y; x = x % y; x = x y; x = x y; x+; x-- x = *y; x = -y; // Relacina les y lgicos: f (x > y) ; f (x >= y) ; f (x < y) ; f (x <= y); f(x == y); f(x i* y); //! (ix) ; //! f (X & & y) ;

//i f(x || y); // Operadore s bit a bit: x = -y; x = x & y; x = X I y; x a x * y; x * x 1; x = x 1; x x > X // Asignac

3 Operadores 195 in compues ta: x += y; x y; x *= y; x /= y; x %= y; X c<= 1;

X * ; X >s 1; x y; x A= y; x |* y; // Proyeccin: //] boolean bl = (boolean)x; char c (char)x/ byte b * (byte)x; short s (short)x; nt i (int)x; float f = (float)x; double d - (double)x;

X++; x--; x +y i x <= -y; // Relacina

3 Operadores 196 les y lgicos: f Ix > y); f Ix >- y) i t (x < y) ; f lx <= y); Cx ~ y)t f (x !* y) ,* l/\ f (1X) ;

//l fIX && y); //I t(X || y); // Operadores bit a bit: //I X * X > lj // Asign acin compu esta: x y; x y; x *y; x /= y; x %= y; //i X * lj // t x >> 1; //I X > I; // X &= y; //! X y; //I x I* y; // Proyeccin: //! boolean bl = (boolean)x; char c * (char)x; byte b (byte'x; short s (short)x; int \ = lintx; long 1 = (long)x; double d (double)x/ void doubleTest(double x, double y) { // Operadores aritmticos: X < =

3 Operadores 197 X * y ; x e x i y ; x * x % y ; x * x y ; x x y ; X + *

3 Operadores 198 X x * + y ; x * y ; // Relacinales lgicos: f (x > y) f Ix >= y) \ f (x < y) j f (x < y); fx == y)/ fix ! = y); //i f(!x); //1 f (x && y) ; // f(x || y); // Operadores bit bit: // x = x > 1; / / A s i g n a c i n c o m p u e s t a

3 Operadores 199 : x y ; x = y ; x y ; x / = y ; x % = y ; //} //! //I //! x X x X <<= 1; 1; >>>* 1; y?

//! Xa y; //I X | y; // Proyeccin: //! boolean bl (boolean!x; char c ** (charjx;

3 Operadores 200 byte b = (byte)x; short s * i short)x; int i * (intlx; long 1 (long)x; float f * I float)x;

} ///:-

Observe que boolean es bastantelimitado A una variable de este tipo se le pueden asignar los valores true y false, y se

3 Operadores 201 puede comprobar si el valor es verdadero o falso. |>ero no se pueden sumar valores booleanos ni realizar ningn otro tipo de

operacin con ellos.

En char, byte v short, puede ver el efecto de la promocin con los operadores aritmticos. Toda operacin aritmtica sobre cualquiera de estos tipos genera un resultado int. que despus debe ser proyectado explcitamente al tipo original (una conversin de estrechamiento que puede perder informacin) para realizar la asignacin a dicho tipo. Sin embargo, con los valores in( no es necesaria una proyeccin, porque todo es ya de tipo int. Sin embargo, no se crea que todas las operaciones son seguras. Si se multiplican dos valores int que sean lo suficientemente grandes, se producir un desbordamiento en el resultado. como se ilustra en el siguiente ejemplo: //: operators/Overflowoava // i Sorpresa: Java permite los desbordamientos. public class Overflow | pu b l

3 Operadores 202 i c s t a t i c v o i d m a i n { S t r i n g [ ] a r g s ) { i n t b i g I n t e g e r . M A X

3 Operadores 203 _ V A L U E ; S y s t e m . o u t . p r i n t l n ( M b i g * M * b i g ) ; i n t b i g g e r *

3 Operadores 204 b i g 4 System.out.println("b igger * " biggerl ;

) ) / O u t p u t : b i g = 2 1 4 7 4 8 3 6 4 7 b i

3 Operadores 205 g g e r 4 / / / : -

No se obtiene ningn tipo de error o advertencia por pane del compilador, y tampoco se genera ninguna excepcin en tiempo de ejecucin. El lenguaje Java es muy bueno, aunque no hasta ese punto.

Las asignaciones compuestas no requieren proyecciones para chur, byte O short. an cuando estn realizando promociones que provocan los mismos resultados que las operaciones aritmticas directas. Esto resulta quiz algo sorprendente pero, por otro lado, la posibilidad de no incluir la proyeccin simplifica el cdigo.

3 Operadores 206 Como puede ver. con la excepcin de boolean, podemos proyectar cualquier tipo primitivo sobre cualquier otro tipo primitivo. De nuevo, recalquemos que es preciso tener en cuenta los efectos de las conversiones de estrechamiento a la hora de realizar proyecciones sobre tipos de menor tamao; en caso contrario, podramos perder informacin inadvertidamente durante la proyeccin.

Ejercicio 14: (3) Escriba un mtodo que tome dos argumentos de tipo String y utilice todas las comparaciones

boolean para comparar las dos cadenas de caracteres e imprimir los resultados. Para las comparaciones = y !=, realice tambin la prueba con equals( ) Fn main( ). invoque el mtodo que haya escrito, utilizando varios objetos String diferentes.

3 Operadores 207 Resumen

Si tiene experiencia con algn lenguaje que emplee una sintaxis similar a la de C, podr ver que los operadores de Java son tan similares que la curva aprendizaje es prcticamente nula. Si este capitulo le ha resultado difcil, asegrese de echar un vistazo a la presentacin multimedia Thinking n C. disponible en wnr MindVtew.net. Puede encontrar las soluciones a los ejercicios seleccionados en el documento elecixonico 77>r Thinkiitg tn Java Annonital So/unon Uutde. que esta disponible para la venta en MiadYien'.ne

tControl de ejecucin

Al igual que las criaturas sensibles, un programa debe manipular su mundo y tomar decisiones durante la ejecucin. En Java, las decisiones se toman mediante las instrucciones de control de ejecucin.

Java utiliza todas las instrucciones de control de ejecucin de C. por lo que si ha programado antes con C o 0+, la mayor parte de la informacin que vamos a ver en este capitulo le resultar familiar. La mayora de los lenguajes de programacin procedimental disponen de alguna clase de instrucciones de control, y suelen existir solapamientos entre los distintos lenguajes. En Java, las palabras clave incluyen If-else. while, do-whUe, for. return. break y una instruccin de seleccin denominada switch Sin embargo. Java no soporta la despreciada instruccin goto (que a pesar de ello en ocasiones representa la forma ms directa de resolver cienos tipos de problemas). Se puede continuar realizando un salto de estilo goto, pero est mucho ms restringido que un j*oto tpico. true y false

Todas las instrucciones condicionales utilizan la veracidad o falsedad de una expresin condicional para determinar la ruta do ejecucin. Un ejemplo de expresin condicional sera a = b. Aqu, se utiliza el operador condicional = para ver si el valor de a es equivalente al valor de b La expresin devuelve true o false Podemos utilizar cualquiera de los operadores relacinales que hemos empleado en el capitulo anterior para escribir una instruccin condicional. Observe que Java no permite utilizar un nmero como boolean. a diferencia de lo que sucede en C y C++ (donde la veracidad se asocia con valores distintos cero > la falsedad con cero). Si quiere emplear un valor no boolean dentro de una prueba boolean. como por ejemplo if(a), deber primero convertir el valor al tipo boolean usando una expresin condicional, como por ejemplo if(a != 0)

if-else

La instruccin If-else representa la forma ms bsica de controlar el flujo de un programa. La clusula else es opcional, por lo que se puede ver if de dos formas distintas: if(expresinbooleanainstruccin

o if(expresinbooleana) instruccin else instruccin

La i'.xpresin-boo/eana debe producir un resultado boolean. La instruccin puede ser una instruccin simple terminada en punto y coma o una instruccin compuesta, que es un gnipo de instrucciones simples encerrado entre llaves. All donde empleemos la palabra instruccin quenemos decir siempre que esa instruccin puede ser simple o compuesta.

Como ejemplo de If-else. he aqu un mtodo test( ) que indica s un cierto valor est por encima, por debajo o es equivalente a un nmero objetivo:

/ / : control/IfElse - java import static net.mindview.util.Print. ; public class IfElse { static int result = 0; static void test(int testval. int target) { if(testval > target) result !; else if(testval < target) result -1; else result * 0; // Coincidencia

) public static void main(Stri ng[] args) { test(10 , 5); print (result) ; test(5, 10); print(res ult); test 5. 5) ; print (result) ;

} ) /* Output:

1 -1 0

///:-

En la pane central dc test( ), tambin puede ver una instruccin "else if," que no es una nueva palabra clave sino una instruccin else seguida de una nueva instruccin If.

Aunque Java, como sus antecesores C y C++, es un lenguaje dc formato libre* resulta habitual sangrar el cuerpo de las ins- tnicciones de control de flujo, para que el lector pueda determinar fcilmente dnde comienzan y dnde terminan. Iteracin

Los bucles de ejecucin se controlan mediante while, do-while v for, que a veces se clasifican como instrucciones tic iteracin, Una determinada instruccin se repite hasta que la expresin-hooleana de control se evale como false. La forma de un bucle while es:

whil#iexpresin-booleana instruccin

La expresin-booleana se evala una vez al principio del bucle y se vuelve a evaluar antes de cada sucesiva iteracin de la instruccin.

He aqui un ejemplo simple que genera nmeros aleatorios hasta que se cumple una determinada condicin. //: control/WhileTest.java // Ilustra el bucle while. public class WhileTest ( static boolean conditionO ( boolean result Math.random() < 0.99; System, out .print (result * ", return result;

) public static void main(String[] args) { while(condition()) System.out.println("Inside 'while*); System.out.orintIn I"Exited 'while'");

) ) /* (Ejectelo para ver la salida) ///:-

El mtodo condition( ) utiliza el mtodo rundnm( ) de tipo static de la biblioteca Math. que genera un valor douhle comprendido entre 0 y l (incluye 0, pero no 1.) El valor result proviene del operador de comparacin <, que genera un resultado de tipo boolean. Si se imprime un valor boolean, automticamente se obtiene la cadena de caracteres apropiada iruc**

o false. La expresin condicional para el bucle while dice; repite las instrucciones del cuerpo mientras que coadition( ) devuelva true* do-while

La forma de dowhile os do instruccin while {expresinboclear.al ;

La nica diferencia entre while y do-while es que la instruccin del bucle do-whlc siempre se ejecuta al menos una vez. me luso aunque la expresin se evale como false la primera vez. En un bucle while. si la condicin es false la primera vez. la instruccin nunca llega a ejecutarse. En la

prctica, do-while es menos comn que while. for

El bucle for es quiz la forma de iteracin ms habitualmente utilizada. Este bucle realiza una inicializacin antes de la primera iteracin. Despus realiza una prueba condicional y. al final de cada iteracin, lleva a cabo alguna forma de avance de paso*. La forma del bucle for es: for(inicializacin; expresinbooleana; paso) instruccin

Cualquiera de las expresiones inicializacin. expresin-booleana o paso puede estar vaca. La expresin booleana se comprueba antes de cada iteracin y. en cuanto se evale como false. la ejecucin contina en la lnea que sigue a la instruccin for Al final de cada bucle, se ejecuta el paso.

Los bucles for se suelen utilizar para tareas de 'recuento": //: control/ListCharacters.java // Ilustra los bucles for'* enumerando ( f todas las letras minsculas ASCII. public class ListCharacters ( public etatic void maintCtringU arasi | for(char c = 0/ c c 128 ) ir (Character.isLowerCase{ c)} System.out .println("valu: " + (int)c " character: * * c);

Observe que la variable c se define en el mismo lugar en el que se la utiliza, dentro de la expresin de control correspon diente al buele for. en lugar de definirla al principio de maini ). El mbito de c es la instruccin controlada por for.

Este programa tambin emplea la elasc envoltorio" java.lang.Character. que no slo envuelve el tipo primitivo char dentro de un objeto, sino que tambin proporciona otras utilidades Aqui el mtodo stutic isLo\verCase( ) se usa para detectar si el carcter en cuestin es una letra minscula.

Los lenguajes proceditnentales tradicionales como C requieren que se definan todas las variables al comienzo de un bloque, de modo que cuando el compilador crec un bloque, pueda asignar espacio para esas variables En Java y C++-, se pueden distribuir las declaraciones de variables por todo el bloque, definindolas en el lugar que se las necesite. Esto permite un estilo ms natural de codificacin y liace que el cdigo sea ms fcil de entender.

Ejercicio 1:

(l) Escriba un programa que imprima los valores comprendidos entre I y 100.

Ejercicio 2: (2) Escriba un programa que genere 25 valores int aleatorios. Para cada valor, utilice una instruccin

if-clse para clasificarlo como mayor que. menor que o igual a un segundo valor generado aleatoriamente.

Ejercicio 3: (l) Modifique el Ejercicio 2 para que el cdigo quede rodeado por un bucle hile infinito De este mo

do, el programa se ejecutar hasta que lo interrumpa desde el teclado (normalmente, pulsando Control-C).

Ejercicio 4: (3) Escriba un programa que utilice dos bucles for anidados y el operador de mdulo !%) para detectar e

imprimir nmeros primos (nmeros enteros que no son divisibles por ningn nmero excepto por s mismos y por 1).

Ejercicio 5: (A) Repita el Ejercicio 10 del capitulo anterior, utilizando el operador ternario y una comprobacin de tipo

bit a bit para mostrar los unos y ceros en lugar de IntegertoBinarv-Strin^ ). El operador coma

Anteriormente en el capitulo, hemos dicho que el operador coma (no el separador coma que se emplea para separar definiciones y argumentos de mtodos) slo tiene un uso en Java: en la expresin de control de un bucle for. Tanto en la parte correspondiente a la inicializacin como en la parte correspondiente al paso de la expresin de control, podemos incluir una serie de instrucciones separadas por comas, y dichas instrucciones se evaluarn secuencialmente.

Con el operador coma, podemos definir mltiples variables dentro de una instruccin for. pero todas ellas deben ser del mismo tipo: //: control/CommaOperator.java public clasa CommaOperator ( public static vod mainString[J args) { for(int i 1, j * i * 10; i < 5; i+*, j i * 2) {

///:-

La definicin int de la instruccin for cubre tanto a i como a j La pane de inicializacin puede tener cualquier nmero de definiciones de un mismo Upo. La capacidad de definir variables en una expresin de control est limitada a los bucles for. No puede emplearse esta tcnica en ninguna otra de las restantes instrucciones de seleccin o iteracin.

Puede ver que. tanto en la parte de inicializacin como en la de paso, las instrucciones se evalan en orden secucncial. Sintaxis foreach

Java SE5 introduce una sintaxis for nueva, ms sucinta, para utilizarla con matrices y contenedores (hablaremos ms en detalle sobre este tipo de objetos en los Captulos 16. Matrices, y 17, Anlisis detallado de tos contenedores). Esta sintaxis se denomina sintaxis foreach (para todos), y quiere decir que no es necesario crear una variable int para efectuar un recuento a travs de una secuencia de elementos: el bucle for se encarga de generar cada elemento automticamente.

por ejemplo, suponga que tiene una matriz de valores float y que quiere seleccionar cada uno de los elementos de la matriz:

//: control/ForEachFloat.java java.util.#;

import

public elass ForEachFloat {

public static void main(StringfJ args) (

Random rand = new Random(47); float fIJ = new float[10]; forint i a 0; i < 10; i++) f[i] = rar.d .nextFloat () j for(float x : i)

System.out.println(x);

} / Output:

0.72711575

0.39982635

0.5309454

G.0534122

0.16Q2Q656

0.57799757

0.18847865

0.4170137

0.51660204

0.73734957

La matriz se rellena utilizando el antiguo bucle for, porque debe accederse a ella mediante un ndice. Puede ver la sintaxis foreach en la linea:

for(float x : f) (

Esto define una variable x de tipo float y asigna secucncialmente cada elemento de f a x.

Cualquier mtodo que devuelve una matriz es buen candidato para emplearlo con la sintaxis foreach. Por ejemplo, la clase String tiene un mtodo toCharArrayt ) que devuelve una matriz de char. por lo que podemos iterar fcilmente a iraves de los caracteres de una matriz:

//: control/ForEachString.java

public class Korfcachstring {

public static void mainString[] args) {

forchar c : "An African Swallow".toCharArray() J System.out.print(c H ");

} J /* Output:

An A f r i c a n ///:-

S w a l l o w

Como podremos ver en el Captulo 11. Almacenamiento Je objetos, la sintaxis fotvach tambin funciona con cualquier objeto que sea de tipo Iterable.

Muchas instrucciones for requieren ir paso a paso a travs de una secuencia de valores enteros como sta:

fortint i = 0; i < 100; i** 1

Para este tipo de bloques, la sintaxis foreach no funcionar a menos que queramos crear primero una matriz de valores nt. Para simplificar esta tarea, he creado un mtodo denominado range() en nct.mindview.util.Range que genera automticamente la matriz apropiada. La intencin es que range ) se utilice como importacin de tipo static:

//: control/ForEachlnt.java

import static net.mindview.util.Range.;

public class ForEachlnt (

import static net .mindview.util.Print. *,*

El mtodo rangc( ) est sobrecargado, Id 6 7 8 que quiere decir que puede utilizarse el ///:mismo mtodo con diferentes listas de argumentos (en breve hablaremos del mecanismo de sobrecarga). La primera forma sobrecargada de range( ) empieza en cero y genera valores hasta el extremo superior del rango, sin incluir ste. La segunda forma comienza en el primer valor y va hasta un valor menos que el segundo, y la lercera forma incluye un valor de paso, de modo que los incrementos se realizan segn ese valor. range( ) es una versin muy simple de lo que se denomina generador, que es un concepto del que hablaremos posteriormente en el libro.

i ) /* Output: 0123456*7139 5 9 S B 11 14 17

Observe que aunque range() permite el uso de la sintaxis foreach en ms lugares, mejorando asi la legibilidad del cdigo, es algo menos eficiente, por lo que se est utilizando el programa con el Un de conseguir la mxima velocidad, conviene que utilice un perfilador, que es una herramienta que mide el rendimiento del cdigo.

Podr observar tambin el uso de prmtnh() adems de print( ). El mtodo printnb( ) no genera un carcter de nueva linea, por lo que permite escribir una linea en sucesivos fragmentos.

La sintaxis foreach no slo ahorra tiempo a la hora de escribir el cdigo. Lo ms importante es que facilita la lectura y comunica perfectamente qu es lo que estamos tratando de hacer (obtener cada elemento de la matriz) en lugar de proporcionar los detalles acerca de cmo lo estamos haciendo (Estoy creando este ndice para poder usarlo en la seleccin de cada uno de los elementos de la matriz") Utilizaremos la sintaxis foreach siempre que sea posible a lo largo del libro. return

Diversas palabras clave representan lo que se llama un salto incondicional, lo que simplemente quiere decir que el salto en el flujo de ejecucin se produce sin realizar previamente comprobacin alguna. Dichas palabras clave incluyen return. break. continu y una forma de saltar a una instruccin etiquetada de forma similar a la instruccin goto deotTos lenguajes.

La palabra clave return tiene dos objetivos: especifica qu valor devolver un mtodo (si no tiene un valor de retomo de tipo vold) y hace que la ejecucin salga del mtodo actual devolv iendo ese valor.

Podemos reescribir el mtodo test() precedente para aprovechar esta caracterstica: //: control/IfElse2.java import static net .mindview. til. Prir.t. public class IBlse2 ( static int test(int testval, int target) ( iftestval > target) return *-1; else iftestval < target) return -1; else return 0; // Coincidencia

4 Control de ejecucin 227 )public static void main(StringlJ args) ( print(test(10, 5}); print(test(5, 10)); print(test(5, 5)) ;

} ) /* Output: 1 -1 0

*///:-

No hay necesidad de la clusula else, porque el mtodo no continuar despus de ejecutar una instruccin retum.

Si no incluye una instruccin return en un mtodo que devuelve un valor void. habr una instruccin return implcita al final de ese mtodo, asi que no siempre es necesario incluir dicha instruccin. Sin embargo, si el mtodo indica que va a devolver cualquier otro valor distinto de void. hay que garantizar que todas las rutas de ejecucin del cdigo devuelvan un valor.

4 Control de ejecucin 228 Ejercicio 6: (2) Modifique los dos mtodos test( ) de los dos programas anteriores para que admitan dos argumentos

adicionales, begin y cnd. y para que se compruebe lesiva! para ver si se encuentra dentro del rango comprendido entre begin y end (ambos incluidos). break y continu

Tambin se puede controlar el flujo del bucle dentro del cuerpo de cualquier instruccin de iteracin utilizando break y continu. break provoca la salida del bucle sin ejecutar el resto de la instrucciones. La instruccin continu detiene la ejecucin de la iteracin actual y vuelve al principio del bucle para comenzar con la siguiente iteracin

Este programa muestra ejemplos de break y continu dentro de bucles for y while //; control/BreakAndContinue.java // Ilustra las palabras clave break y continu. import static net.mindview.util.Range. public class BreakAndContinue { public static void mam(String [] args) ( for lint i * 0; i < 100; i+-*-J ( if(i if(i ** 74) V 9 break; // Fuera 0) continu; // del bucle Siguiente iteracin

System.out.print(i + " ");

4 Control de ejecucin 229 i System.out.pri ntln( ) l f Oso de foreach: forint i : range(100)J { if(i i(i ** 74) % 9 !* break; // Fuera 0) continu; // del bucle Siguiente iteracin

System.out .orint (i '*

) System.out.println{); int i 0; // Un bucle infinito": while(true) { i-f*; int j * i 27; if(j == 1269) break; // Fuera del bucle for if(i % 10 !* 0) continu; // Principio del bucle System.out .print (i -+ ' H);

) ] /* Output: O 9 10 27 36 45 54 63 72

4 Control de ejecucin 230 O 9 18 27 36 45 54 63 72 10 20 30 40 *///:-

En el bucle for. el valor de I nunca llega a 100, porque la instruccin break hace que el bucle termine cuando vale 74. Normalmente, utilizaremos una instruccin break como sta slo si no sabemos cundo va a cumplirse la condicin de terminacin. La instruccin continu hace que la ejecucin vuelva al principio del bucle de iteracin (incrementando por tanto i) siempre que i no sea divisible por 9. Cuando lo sea, se imprimir el valor.

El segundo bucle for muestra el uso de la sintaxis foreach y como esta produce los mismos resultados.

Finalmente, podemos ver un bucle while infinito que se estara ejecutando, en teora, por siempre Sin embargo, dentro del bucle hay una instruccin break que har que salgamos del bucle. Adems, podemos ver que la instruccin continu devuelve el control al principio del bucle sin ejecutar nada de lo que hay despus de dicha instruccin continu (por tanto, la impresin slo se produce en el segundo bucle cuando el valor de i es divisible por 10). En la salida, podemos ver que se imprime el valor 0. porque 0 % 9 da como resultado 0.

Una segunda forma del bucle infinito es for(;;). El compilador trata tanto whilc(true) como for(;;) de la misma forma, por lo que podemos utilizar una de las dos formas segn prefiramos.

4 Control de ejecucin 231 Ejercicio 7: (1) Modifique el Ejercicio I para que el programa termine usando la palabra clave break con el valor 99.

Intente utilizar return en su lugar. La despreciada instruccin goto

La palabra clave goto ha estado presente en muchos lenguajes de programacin desde el principio de la Informtica. De hecho, goto represent la gnesis de las tcnicas de control de programa en los lenguajes ensambladores: Si se cumple la condicin A, salta aqu; en caso contrario, salta all*. Si leemos el cdigo ensamblador generado por casi todos los compiladores, podremos ver que el control de programa contiene muchos saltos (el compilador Java produce su propio cdigo ensamblador", pero este cdigo es ejecutado por la mquina virtual Java en lugar de ejecutarse directamente sobre un procesador hardware).

Una instruccin goto es un salto en el nivel de cdigo fuente, y eso es lo que hizo que adquiriera una mala reputacin. Si un programa va a saltar de un punto a otro, no existe alguna forma de reorganizar el cdigo para que el flujo de control no tenga que dar saltos? La instruccin goto lleg a ser verdaderamente puesta en cuestin con la publicacin del famoso articulo "Gota consi dered harmfur de Edsger Dijkstra. y desde entonces la caza del goto se ha convertido en un deporte muy popular, forzando a los defensores de esa instruccin a ocultarse cuidadosamente.

4 Control de ejecucin 232 Como suele suceder en casos como ste, la verdad est en el punto medio. El problema no est en el uso de goto, sino en su abuso, en determinadas situaciones especiales goto representa, de hecho, la mejor forma de estructurar el flujo.

Aunque goto es una palabra reservada en Java, no se utiliza en el lenguaje. Java no dispone de ninguna instruccin goto. Sin embargo, si que dispone de algo que se asemeja a un salto, y que est integrado dentro de las palabras clave break y continu. No es un salto, sino ms bien una forma salir de una instruccin de iteracin. La razn por la que a menudo se asocia este mecanismo con las discusiones relativas a la instruccin goto es porque utiliza la misma tcnica: una etiqueta.

Una etiqueta es un identificador seguido de un carcter de dos puntos, como se muestra aqu: labeli:

Ll nico lugar en el que una etiqueta resulta til en Java es justo antes de una instruccin de iteracin. V queremos decir exactamente justo antes: no resulta conveniente poner ninguna instruccin entre la etiqueta y la iteracin. Y la nica razn para colocar una etiqueta en una iteracin es si vamos a anidar otra iteracin o una instruccin switch (de la que hablaremos enseguida) dentro de ella. Esto se debe a que las palabras clave break y continu normalmente slo interrumpirn el bucle actual, pero cuando se las usa como una etiqueta interrumpen todos los bucles hasta el lugar donde la etiqueta se haya definido: labell: iteracin-externa { iteracin-interna (break; // l) continue. // (2)

4 Control de ejecucin 233 //... continue label1; // 13)

//... break labell; // 14)

En ( I ) , la instruccin break hace que se salga de la iteracin iniema y que acabemos en la iteracin externa. En (2). la instruccin continue hace que volvamos al principio de la iteracin interna. Pero en (3). la instruccin continue label I hace que se salga de la iteracin interna r de la iteracin externa, hasta situarse en labell Entonces, contma de hcchocon la iteracin. pero comenzando en la iteracin extema. En (4). la instruccin break label I tambin hace que nos salgamos de las dos iteraciones hasta situamos en labell. pero sin volver a entrar en la iteracin. De hecho, ambas iteraciones habrn finalizado.

4 Control de ejecucin 234 He aqu un ejemplo de utilizacin de bucles for //: control/LabeledFor.java // Bucles for con "break eqtiquetado" y continue etiquetado", import static net.raindview.util.Print.; public class LabeledFor ( public static void main(StringH args) { int i = 0; outer: // Aqu no se pueden incluir instrucciones fori; true ;) ( // bucle infinito inner: // Aqu no se pueden incluir instrucciones for(; i < 10; i++) ( print(Mi = M i); if(i 2) { print{ cont inue"); continue;

} if (i == 3) { print "break"); i++; // En caso contrario, nunca // se incrementa. break;

) if (i = 7) { print("continue outer); i+4-; f t En caso contrario, i nunca // se incrementa, continue

4 Control de ejecucin 235 outer;

i if (i 8) ( print{"break outer"); break outer;

i for(int k 0; k < 5/ k++) { if (k *= 3) ( print("continue inner"); continue inner; }// Aqu no se puede ejecutar break o continue cara saltar a etiquetas I

Observe que break hace que salgamos del bucle for. y que la expresin de incremento no se ejecuta

4 Control de ejecucin 236 hasta el final de la pasada a travs del bucle for. Puesto que break se salta la expresin incremento, el incremento se realiza directamente en el caso de i = 3. La instruccin continu outer en el caso de i = 7 tambin lleva al principio del bucle y tambin se salta el incremento, por lo que en este caso tenemos tambin que realizar el incremento directamente.

S no fuera por la instruccin break outer no habra forma de salir del bucle externo desde dentro de un bucle interno, ya que break por si misma slo permite salir del bucle ms interno (lo mismo cabria decir de continu).

Por supuesto, en aquellos casos en que salir de un bucle implique salir tambin del mtodo, basta con ejecutar return

Me aqu una demostracin de las instrucciones break y continu etiquetadas con bucles while: //: control/LabeledWhile.java // Bucles while con "break etiquetado" y "continu etiquetado", import static nec.mindview.util.Print.; public clacc LabelcdWhile { public static void man(Stringll args) { int i * Oj outer: whiletrue) ( print("Outer while loop"),while(txue> { i+*; print i "i = " i}; ifU ** 1) { print("conti nu"); cont inue

4 Control de ejecucin 237 ) if(i 3) l print("continu outer"); continu outer;

} if ti 5) { print("break ")? break;

} if(i 7)

i 4 Control de ejecucin 238 {print <"break outer"); break outer;

) } / Output: Outer while loop 1*1 continu i = 2 i - 3 continu outer Outer while loop i * 4 i = 5 break Outer while loop i * 6 i = 7 break outer ///:-

i 4 Control de ejecucin 239 Las mismas reglas siguen siendo ciertas para hile

Una instruccin continu normal hace que saltemos a la parte superior del bucle ms interno y continuemos alli la ejecucin.
1.

Una instruccin continu etiquetada hace que saltemos hasta la etiqueta y que volvamos a ejecutar el bucle situado justo despus de esa etiqueta.
2.

3.

Una instruccin break hace que finalice el bucle.

4.

Una instruccin break etiquetada hace que finalicen todos los bucles hasta el que tiene etiqueta, incluyendo

la

este ltimo.

i 4 Control de ejecucin 240 Ls importante recordar que la nica razn para utilizar etiquetas en Java es si tenemos bucles anidados y queremos ejecutar una instruccin break o continu a travs de ms de un nivel.

En el artculo "Goto considcredharmfur de Dijkstra. la objecin especifica que l haca era contra la utilizacin de etiquetas no de la instruccin goto. Su observacin era que el nmero de errores pareca incrementarse a medida que lo haca el nmero de etiquetas dentro de un programa, y que las etiquetas en las instrucciones goto hacen que los programas sean mas difciles de analizar. Observe que la etiquetas de Java no presentan este problema, ya que estn restringidas en cuanto a su ubicacin y pueden utilizarse para transferir el control de forma arbitraria. Tambin merece la pena observar que este es uno de esos casos en los que se hace ms til una determinada caracterstica del lenguaje reduciendo la potencia de la correspondiente instruccin. switch

La palabra clave switch a veces se denomina instruccin de seleccin. 1.a instruccin switch permite seleccionar entre distintos fragmentos de cdigo basndose en el valor de una expresin entera. Su forma general es: switch(selector-entero) { case valor-enterol : instruccin; break; case valor-entero2 : instruccin; break; case valor-entero3 : instruccin; break; case valor-entero* s instruccin; break; case valor-enteroS : instruccin; break; default: instruccin;

i 4 Control de ejecucin 241

Selector-entera es una expresin que genera un valor entero. La instruccin swilch compara el resultado de selector-entero con cada valor-entero Si encuentra una coincidencia, ejecuta la correspondiente instruccin (una sola instruccin o mltiples instrucciones: no hace falta usar llaves). Si no hay ninguna coincidencia, se ejecuta la instruccin de default

Observar en la definicin anterior que cada case finaliza con una instruccin break. lo que hace que la ejecucin salte al final del cuerpo de la instruccin switeh. sta es la forma convencional de construir una instruccin switeh, pero la instruccin break es opcional. Si 110 se incluye, se ejecutar el cdigo de las instrucciones case situadas a continuacin hasta que se encuentre una instruccin break. Aunque normalmente este comportamiento no es el deseado, puede resultar ltil en ocasiones para los programadores expertos. Observe que la ltima instruccin, situada despus de la clusula default, no nene una instruccin break porque la ejecucin contina justo en el lugar donde break baria que continuara. Podemos incluir, sin que ello represente un problema, una instruccin break al final de la clusula default si consideramos que resulta importante por razones de estilo.

La instruccin switeh es una forma limpia de implementar selecciones multiva (es decir, selecciones donde hay que elegir entre diversas rutas de ejecucin), pero requiere de un selector que se evale para dar un valor entero, como int o char. Si se desea emplear, por ejemplo, una cadena de caracteres o un nmero en coma flotante como selector, no funcionar en una instruccin switeh Para los tipos no enteros, es preciso emplear una serie deinstrucciones if. Al final delsiguiente capitulo. veremos que la nueva caracterstica enum de Java SE5 ayuda a suavizar esta restriccin, ya que los valores enuni estn

i 4 Control de ejecucin 242 diseados para funcionar adecuadamente con la instruccin switeh.

He aqu un ejemplo en el que se crean letras de manera aleatoria y se determina si son vocales o consonantes: //: control/VowelsAndConsonants.java // Ilustra la instruccin switeh. import j ava.til.; import static net .tr.indview. til .Print. * ; public class VowelsAndConsonants { public static void roain(String[] argsi { Random rand = new Random(4 7) 7 for(int i 0; i < 100; i+4) { int c = rand. next Int (26) ' a'.printnb { (char) c + ", " + c + *:

) switeh(c) {

i 4 Control de ejecucin 243

} } /* Output: y, 121: Sometimes a vowel n, 110: consonant z, 122: consonant b, 98: consonant r, 11*5: consonant n, 110: consonant y, 121: Sometimes a vowel g. 103: consonant c, 59: consonant f, 102: consonant o, 111: vowel

i 4 Control de ejecucin 244

i 4 Control de ejecucin 245 w, 119: Sometimes a vowel 2. 122: consonant

///:-

Puesto que Kandom.ncxtlnf(26) genera un valor comprendido entre y 26. basta con sumar a' para generar las letras minsculas. Los caracteres encerrados entre comillas simples en las instrucciones case tambin generan valores, enteros que se emplean para comparacin.

Observe cmo las instrucciones case pueden apilarse" unas encima de otras para proporcionar mltiples coincidencias para un determinado fragmento de cdigo Tenga tambin en cuenta que resulta esencial colocar la instruccin break al final de una clusula case concreta; en caso contrario, el control no efectuarla el salto requerido y continuara simplemente procesando el caso siguiente.

Ln la instruccin: nt c rand.nextlnt (26)

i 4 Control de ejecucin 246 Kandom.nexf lnt( ) genera un valor int aleatorio comprendido entre 0 y 25, al que se le suma el valor a*. Esto quiere decir que a* se convierte automticamente a int para realizar la suma.

Para imprimir c como carcter, es necesario proyectarlo sobre el tipo char; en caso contrario, generara una salida de tipo entero.

Ejercicio 8: (2) Cree una instruccin switch que imprima un mensaje para cada case, y coloque el switch dentro de un

bucle for en el que se pruebe cada uno de los valores de case. Incluya una instruccin break despus de cada case y compruebe los resultados; a continuacin, elimine las instrucciones break y vea lo que sucede.

Ejercicio 9: (4) Una Secuencia Je Fihonacci es la secuencia de nmeros I. I, 2, 3. 5. 8. 13, 21, 34, etc., donde cada

i 4 Control de ejecucin 247 nmero (a partir del tercero) es la suma de los dos anteriores. Cree un mtodo que tome un entero como argumento y muestre esa cantidad de nmeros de Fihonacci comenzando por el principio de la secuencia; por ejemplo, si ejecuta java Fibonacci 5 (donde Fihonacci es el nombre de la clase) la salida seria: I. I.

2,

3, 5.

Ejercicio 10: (5) Un nmero vampiro tiene un nmero par de dgitos y se forma multiplicando una pareja de nmeros

que contengan la mitad del nmero de dgitos del resultado. Los dignos se loman del nmero original en cualquier orden. No se permiten utilizar parejas de ceros finales Entre los ejemplos tendramos:

1260 = 21 * 60 1827 = 21 * 87 2187 = 27 81

i 4 Control de ejecucin 248 Escriba un programa que determine todos los nmeros vampiro de 4 dgitos (problema sugerido por Dan Forhan). Resumen

Este capitulo concluye el estudio de las caractersticas fundamentales que podemos encontrar en la mayora de los lenguajes de programacin: calculo, precedencia de operadores, proyeccin de lipos y mecanismos de seleccin e iteracin. Ahora estamos listos para dar los siguientes pasos, que nos acercarn al mundo de la programacin orientada a objetos F.l siguiente captulo cubrir las importantes cuestiones de la inicializacin y limpieza de objetos, a lo que seguir, en el siguiente capitulo. el concepto esencial de ocultacin de la implementacin.

Pueden encontrarse las soluciones a los ejercicios seleccionados en e! documento electrnico The Thmktnv m Java AmwnticJ Stilurion Guhh. disponible puro la venta en www.Stindllcw.net.Initialization y limpieza

A medida que se abre paso la revolucin informtica, la programacin no segura se ha convertido en uno de los mayores culpables del alto coste que tiene el desarrollo de programas.

Dos de las cuestiones relativas a la seguridad son la inicializacin y la limpieza Muchos errores en C se deben a que el programador se olvida de inicializar una variable, Esto resulta especialmente habitual con las bibliotecas, cuando los usuarios no saben cmo inicializar un componente en la biblioteca, e incluso ni siquiera son conscientes de que deban hacerlo La limpieza tambin constituye un problema especial, porque resulta fcil olvidarse de un elemento una vez que se ha terminado de utilizar, ya que en ese momento deja de preocupamos. Al no borrarlo, los recursos utilizados por ese elemento quedan retenidos y resulta fcil que los recursos se agoten (especialmente la memoria)

C"H- introdujo el concepto de constructor. un mtodo especial que se invoca automticamente cada vez que se crea un objeto. Java tambin adopt el concepto de constructor y adems dispone de un depurador de memoria que se encarga de liberar automticamente los recursos de memoria cuando ya no se los est utilizando. En este capitulo, se examinan las cuestiones relativas a la inicializacin y la limpieza, asi como el soporte que Java proporciona para ambas tarcas

Inicializacin garantizada con el constructor

Podemos imaginar fcilmente que seria sencillo crear un mtodo denominado initialize( ) para todas las clases que escribiramos. El nombre es una indicacin de que es necesario invocar el mtodo antes de utilizar el objeto. Lamentablemente, esto indica que el usuario debe recordar que hay que invocar ese mtodo. En Java, el diseador de una clase puede garantizar la inicializacin de todos los objetos proporcionando un constructor. Si una clase tiene un constructor. Java invoca automticamente esc constructor cuando se crea un objeto, antes incluso de que los usuarios puedan llegar a utilizarlo. De este modo, la inicializacin queda garantizada

La siguiente cuestin es cmo debemos nombrar a este mtodo, y existen dos problemas a este respecto. El primero es que cualquier nombre que usemos podra colisionar con otro nombre que quisiramos emplear como miembro de la clase Fl segundo problema es que debido a que el compilador es responsable de mvocar el constructor, debe siempre conocer qu mtodo invocar. La solucin en O* parece la ms fcil y lgica, de modo que tambin se usa en Java: el nombre del constructor coincide con el nombre de la clase. De este modo, resulta fcil invocar esc mtodo automticamente durante la inicializacin.

He aqu una clase simple con un constructor. //: initialization/SimpleConstructor.java // Ilustracin de un constructor simple. class Rock { Rock O ( // ste es el constructor Syst em.out.print("Rock M);

} public class SimpleConstructor { public static void main(StringU args) ( for (int i * 0; i < 10; i-M-)

new Rock()?

) ) /* Output: Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock ///:-

Ahora, cuando se crea un objeto: new Rock();

se asigna el correspondiente espacio de almacenamiento y se invoca el constructor. De este modo, se garantiza que el objeto esta apropiadamente inicial izado antes de poder utilizarlo.

Observe que el estilo de codificacin consistente en poner la primera letra de todos los mtodos en minscula no se aplica a los constructores, ya que el nombre del constructor debe coincidir exactamente con el nombre de la clase.

Un constructor que no tome ningn argumento se denomina constructor predeterminado. Normalmente, la documentacin de Java utiliza el trmino constructor sin argumentos, pero el trmino 'constructor predeterminado se ha estado utilizando durante muchos aos antes de que Java apareciera, por lo que prefiero utilizar este ltimo trmino. De todos modos, como cualquier otro mtodo, el constructor puede tambin tener argumentos que nos permiten especificar cmo hay que crear el objeto. Podemos modificar fcilmente el ejemplo anterior para que el constructor tome un argumento: // : initialization/SimpleConstructor2.java // Loa constructores pueden tener argumentos. class Rock2 | Rock2 {int i) ( System.out.print("Rock " + i + * *);

} public class SimpleConstructor2 ( public static void main(String[] args) { fortint i * 0; i < 8; new Rock2(i); \ } / Output; Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock S Rock 6 Rock 7 ///:-

Los argumentos del constructor proporcionan una forma de pasar parmetros para la inicializacin de un objeto. Por ejemplo. si la clase Tree (rbol) tiene un constructor que toma como argumento un nico nmero entero que indica la altura del rbol, podremos crear un objeto Tree como sigue:

Tree t = new Tree (12);

// rbol de 12 metros

Si TTee(int) es el nico constructor del que disponemos, el compilador no nos permitir crear un objeto Tree de ninguna otra forma.

Los constructores eliminan una amplia clase de problemas y hacen que el cdigo sea ms fcil de leer Por ejemplo, en el fragmento de cdigo anterior, no vemos ninguna llamada explcita a ningn mtodo initialize( ) que est conccptualmente separado del acto de creacin del objeto. En Java, la creacin y la inicializacin son conceptos unificados: no es posible tener la una sin la otra.

El constructor es un tipo de mtodo poco usual, porque no tiene valor de retorno. Existe una clara diferencia entre esta circunstancia y los mtodos que devuelven un valor de retomo void. en el sentido de que estos ltimos mtodos no devuelven nada, pero seguimos teniendo la opcin de hacer que devuelvan algo. Los constructores no devuelven nada nunca, y no tenemos la opcin de que se comporten de otro modo (la expresin new devuelve una referencia al objeto recin creado, pero el constructor mismo no tiene un valor de retomo). Si hubiera v alor de retomo y pudiramos seleccionar cul es. el compilador necesitaria saber qu hacer con ese valor de retomo.

Ejercicio 1: (I) Cree una clase que contenga una referencia de tipo String no inicializada. Demuestre que esta referen

cia la inicializa Java con el valor nuil

Ejercicio 2: (2i Cree una clase con un campo String que se inicialice en el punto donde se defina, y otro campo que

sea tmcializado por el constructor, (ul es la diferencia entre las dos tcnicas? Sobrecarga de mtodos

t na de las caractersticas ms importantes en cualquier lenguaje de programacin es el uso de nombres. Cuando se crea un objeto, se proporciona un nombre a un rea de almacenamiento. Un mtodo, por su parte, es un nombre que sirve para designar una accin. Utilizamos nombres para referirnos a todos los objeto* y mtodos. Una serie de nombres bien elegida crear un sistema que resultar ms fcil de entender \ modificar por otras personas. En cierto modo, este problema se parece a] acto de escribir literatura, el objetivo es comunicarle con los lectores.

Todos los problemas surgen a la hora de aplicar el concepto de matiz del lenguaje humano a los lenguajes de programacin. A menudo, una misma palabra tiene diferentes significados: es lo que se denomina palabras polisemitas. aunque en el campo de la programacin diramos que estn sobrecargadas Lo que hacemos normalmente es decir "Lava la camisa, Lava el coche" y Lava al perro": seria absurdo vemos forzados a decir "camisaLava la camisa. cocheLava el coche" y perroLava el perro" simplemente para que el oyente no se vea forzado a distinguir cul es la accin que tiene que realizar. La mayora de los lenguajes humanos son redundantes, de modo que podemos seguir determinando el significado aun cuando nos perdamos algunas de las palabras. No necesitamos identificadores unvocos podemos deducir el

significado a partir del contexto.

La mayora de los lenguajes de programacin (> C en particular) exigen que dispongamos de un identificador unvoco para cada mtodo (a menudo denominados funciones en dichos lenguajes). As que no se puede tener una funcin denominada prinM ) para imprimir enteros > otra denominada igualmente print( ) para imprimir nmeros en coma flotante, cada una de las funciones necesitar un nombte distintivo

Ln Java (y en C+^). hay otro factor que obliga a sobrecargar los nombres de los mtodos: el constructor Puesto que el nombre del constructor est predeterminado por el nombre de la clase, slo puede haber un nombre de constructor. Pero entonces. qu sucede si queremos crear un objeto utilizando varias formas distintas* Por ejemplo, suponga que construimos una clase cuyos objetos pueden inicializarse de la forma normal o leyendo la informacin de un archivo, l-larn falta dos constructores. el constructor predeterminado v otro que tome un objeto Strinj como argumento, a travs del cual suministraremos el nombre del archivo que hay que utilizar para inicializar el objeto. Ambos mtodos sern constructores, asi que lendran el mismo nombre: el nombre de la clase. Por tanto, la sobrecarga de mtodos resulta esencial para poder utilizar el mismo nombre de mtodo con diferentes tipos de argumentos Y. aunque la sobrecarga de mtodos es obligatoria para los consiruetores. tambin resulta til de manera general y puede ser empleada con cualquier otro mtodo

Me aqu un ejemplo que muestra tanto constructores sobrecargados como mtodos normales sobrecargados : int iallzat ion /Overloadmg .java Ilustracin dei mecanismo de sobrecarga ! t canco ae conscruccores como ae mtoaos normales _mpcrt sratic r.et .mr.dview.uc il. Print. * . eiass Tree ( int height; Tree() { print t" Plantmg a seedl ing" > ; neight = 0;

) Tree'int mitialHeight ( height - imt lalHeight ? print Creatina new Tree Chat is " -* height - ' feet tall")r

I vold info i) 1 printCTree is u - height * " feet cali"';

} vcid inf o <Str :.ng si { print Is - Tree is " + height * " feet tall"l;

i public class Overloading ( public static void mam (String U args) { forint i 0? i < 5; i-*-+J { Tree t = new Tree(i); t.info(); t.info("overloaded method");

} // Constructor sobrecargado: new Tree();

} } f * Output: Creating new Tree that is 0 feet tall Tree is 0 feet tall overloaded method: Tree is 0 feet tall Creating new Tree that is I feet tall Tree is 1 feet tall overloaded method: Tree is 1 feet call Creating new Tree that is 2 feet tall Tree is 2 feet tall overloaded method: Tree is 2 feet tall Creating new Tree that is 3 feet tall Tree is 3 feet tall overloaded method: Tree is 3 feet tall Creating new Tree that is 4 feet tall Tree is 4 feet tall overloaded method: Tree is 4 feet tall Planting a seedling ///:-

Con estas definiciones, podemos crear un objeto Tree tanto a partir de una semilla, sin utilizar ningn argumento, como en forma de planta criada en vivero, en cuyo caso tendremos que indicar la altura que tiene. Para soportar este comportamiento, hay un constructor predeterminado y otro que toma como argumento la altura del rbol.

Tambin podemos invocar el mtodo nfo() de varias formas distintas. Por ejemplo, si queremos imprimir un mensaje adicional, podemos emplear info(String), mientras que utilizaramos info( ) cuando no tengamos nada ms que decir. Sera bastante extrao proporcionar dos nombres separados a cosas que se corresponden, obviamente, con un mismo concepto. Afortunadamente, la sobrecarga de mtodos nos permite utilizar el mismo mtodo para ambos. Cmo se distingue entre mtodos sobrecargados

Si los mtodos tienen el mismo nombre, cmo puede saber Java a qu mtodo nos estamos refiriendo? Existe una regla muy simple, cada mtodo sobrecargado debe tener una lista distintiva de tipos de argumentos.

Si pensamos en esta regla durante un momento, vemos que tiene bastantes sentido. De qu otro modo podra un programador indicar la diferencia entre dos meiodos que tienen el mismo nombre, si no es utilizando las diferencias entre los tipos de sus argumentos?

Incluso las diferencias en la ordenacin de los argumentos son suficientes para distinguir dos mtodos entre si. aunque normalmente no conviene emplear esta tcnica, dado que produce cdigo difcil de mantener: //: initialization/OverloadingOrder.java // Sobrecarga basada en el orden de los argumentos, import static net.mindview.util.Print.; public class OverloadingOrder { static void f (String s, int i) {

5 Inicializacin y limpieza 260 print("String: H + s 4 ", int: " + i);} static void fint i, String s) ( print (" int: " * i *- ", String: " * a ) ;

i public static void main(Strir.g [J args) ( f("String first, 11Jj f(99, "Int first");

) ) / Output: String: String first, int: 11 int: 99. String: Int first ///:-

Los dos mtodos f( ) tienen argumentos idnticos, pero el orden es distinto y eso es lo que los hace diferentes. Sobrecarga con primitivas

Una primitiva puede ser automticamente convertida desde un tipo de menor tamao a otro de mayor tamao, y esto puede inducir a confusin cuando combinamos este mecanismo con el de sobrecarga. El

5 Inicializacin y limpieza 261 siguiente ejemplo ilustra lo que sucede cuando se pasa una primitiva a un mtodo sobrecargado: //: initialization/PrimitiveOverloadmg. java // Promocin de primitivas y sobrecarga, import static net.raindview.util.Print. # ; public class PrimitiveOverloading { void f3(int x) ( printnb{"f3(int) ") ; } void f3(long x) ( printnb("f3(long) n); ) void f3(float x) [ printnb("f3(float) M); ) void f3(double x) ( printnb("f3(double! w); ) void f4(int xl { printnb("f4Iint) "); ) void f4 (long x) { printnb("f4(long) } void f4(float x) ( printnb("f4(float{ H); ) void f4(double x) ( printnb("f4(double) ") ; ) void f5(long x) { printnb("f5(long) "); } void f5(float x) ( printnb(f5(float) "); } void 5(double x) ( printnb("f5 I double) "l; ) void f6 (float x) ( printnb("f6(float) H); ) void f6(double x) ( printnb(HfS (double) ") ; } void f7(double x) { printnb("f7(double) "); } void testConstVal() ( printnb < "5: ") ,* f 1 (5) ; f 2 (5); f 3 (5);f4(5);fS(S);f6(5);f7(5); print0;

} void teatChar() [ char x = ' x' ? printnb("char: M); fl(x) ; f 2 IX) ;f3(x) ;f4(x);f5(X) ;f6(x) ;f7 Ix) ; print(I ;

5 Inicializacin y limpieza 262 } void testByte{) { byte x = 0; printnbI"byte; "I; f 1 (x) ;f2(x) ; f3 ix) ,*f4 ix) / f5 (x) ;f 6 (x) ; 7 (x) ; print () ;

) void testShortO ( short x = 0; printnb("short: ") ; fl(x); L 2 (x);3(x);f4(x); f S ( x ) ;f6(x);f7(x); print!);

) void testlntU ( int x = 0; printnb{ "int: );

fl(X) ;f2(x) ;f3(x);f4 fX);f5 Ix) ;f6(x) ;f7(x) ; print(); } void testLongO j long x = 0; printnb("long: n); fl(x) ; 2 (x) ; f 3 (x) ; f 4 (x) ; f 5 (x) ; f 6 (x) ;f 7(x) ; printO;

5 Inicializacin y limpieza 263 void testFloat() { float x = 0/ printnbI"float: "); fl (xj ;f2 (x) ; f 3 (x) ,*f4 (x) ; f5 tx) ;f6 (x) ;f7 (x) ; print ( ) ;

J void testDouble() ( double x = 0; printnb("double: ") ; fl (x) ; f2 (x? ; f3 (x) ; f4 (x) ; f5 (x) ;f ix) ;f7 (x) ; print () ;

l public static void main(String I] args) { PrimitiveOverloading p = new PrimitiveOverloading(); p.testConstVal(); p.testChar(); p.testByte (I p.testShort () ; p.testlnt(J ; p.testLong(); p.testFloat I) ; d.testDouble()j

5 Inicializacin y limpieza 264 ) } / Output: 5: fl(int) f2(int > f3(int) f4(int) f5(long) f6(loat? f7(double) char: fl(char) f2(int) f3(int) f4iint> f5(long) f6(float) f7(double) byte: fl(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double) short: fl(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double) int: fl (int) f2(mt) f3(xnt) f4(int) f5(long) f6(float) f7(double) long: fl(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double) float: fl (float) f 2 (float) f3 (float) f4*(float) f5(float> f6(float) f7(double) double: fl(double) f2(double) f3(double) f4(double) f5(double) f6(double' f7(double' ///;-Puede ver que el valor constante 5 se trata como int. por lo que si hay disponible un mtodo sobrecargado que toma un obje- io int. se utilizar dicho mtodo. En todos los dems casos, si lo que tenemos es un tipo de datos ms pequeo que el argumento del mtodo, dicho tipo de datos ser promocionado. char produce un efecto ligeramente diferente, ya que, si no se encuentra una correspondencia exacta con char, se le promociona a int

Qu sucede si nuestro argumento es mayor que el argumento esperado por el mtodo sobrecargado? Una modificacin del programa anterior nos da la respuesta: //: initialization/Demotion.java / Reduccin de primitivas y sobrecarga, import static net.mindview.util.Print.; public class Demotion { void testDoubleO { double x e 0; print("double argument:") ; fllx) ;f2 I (float)* ;f3{(long Ix);4<(int)x); f5((short)x); f 6 [ (byte)x);f7((cha r)x);

) public static void main(String[] args) ( Demotion p = new DemotionO; p.testDoubleO ;

5 Inicializacin y limpieza 265 ) } / Output: double argument: f1idoubl e) f2(float ) f3(long) IA int 1 f5(short i f 6 (byte) f7(char)

*///-

Aqu, los mtodos admiten valores primitivos ms pequeos. Si e! argumento es de mayor anchura, entonces ser necesario efectuar una conversin de estrechamiento mediante una proyeccin Si no se hace esto, el compilador generara un mensaje de error. Sobrecarga de los valores de retorno

Resulta bastante habitual hacerse la pregunta: Por qu slo tener en cuenta los nombres de clase y las listas de argumentos de los mtodos? Por que no distinguir entre los mtodos basndonos en sus valores de retomo? Por ejemplo, los siguientes dos mtodos, que tienen el mismo nombre y los mismos argumentos, pueden distinguirse fcilmente: void f () () int f(} ( retum 1; }

5 Inicializacin y limpieza 266 Esto podra funcionar siempre y cuando el compilador pudiera determinar inequvocamente el significado a partir del contexto, como por ejemplo en int x = f(). Sin embargo, el lenguaje nos permite invocar un mtodo e ignorar el valor de retorno. que es una tcnica que a menudo se denomina invocar un mtodo por su efecto colateral, ya que no nos preocupa el valor de retomo, sino que queremos que tengan lugar los restantes efectos de la invocacin al mtodo. Luego entonces, si invocamos el mtodo de esta forma:

0f

Cmo podra Java determinar qu mtodo f( ) habra que invocar? Y cmo podra determinarlo alguien que estuv iera leyendo el cdigo? Debido a este tipo de problemas, no podemos utilizar los tipos de los valores de retomo para distinguir los mtodos sobrecargados. Constructores predeterminados

Como se ha mencionado anteriormente, un constructor predeterminado (tambin denominado constructor sin argumentos") es aquel que no tiene argumentos y que se utiliza para crear un "objeto predeterminado. Si creamos una c'ase que no tenga constructores, el compilador crear automticamente un constructor predeterminado Por ejemplo: //: initialization/DefaultConstruotor .java class Bird {) publie class DefaultConstructor ( public static void mainlStringH args) { Bird b * new Birdfl; // Default!

5 Inicializacin y limpieza 267 )

) ///:- La expresin new BirdO

crea un nuevo objeto y llama al constructor predeterminado, incluso aunque no se haya definido uno de manera explcita Sin ese constructor predeterminado no dispondramos de ningn mtodo al que invocar para construir el objeto. Sin embargo, si definimos algn constructor (con o sin argumentos), el compilador no sintentkar ningn constructor por nosotros: //: initialization/NoSynthesis.java class 8ird2

5 imcializacion y limpieza 268 (5ii*d2 int i> {) Pird2'double d- (} class NoSynthesis { public static void main (String (J args) ( ' Brd2 b * new Bird2<I; // No hay predeterminado Bird2 b2 = r.ew Blrd2U>? Blrd2 b3 = new Birdl.l;

Si escribimos: new Eira2(i

el compilador nos indicar que no puede localizar ningn constructor que se corresponda con la instruccin que hemos esculo. Cuando no definimos explcitamente ningn constructor, es como si el compilador dijera: "Es necesario utilizar algn constructor, asi que djame definir uno por ti". Pero, si escribimos al menos constructor, el compilador dice: Has escrito un constructor, as que tu sabrs lo que ests haciendo: si no has incluido uno predeterminado es porqti; no quieres hacerlo"

Ejercicio 3: (1) Cree una clase con un constructor predeterminado (uno que no tome ningn argumento) qu imprima

un mensaje. Cree un objeto de esa clase

5 imcializacion y limpieza 269 Ejercicio 4:(11 de tipo String e Aada un constructor sobrecargado al ejercicio anterior que admita un argumento

imprima la correspondiente cadena de caracteres junto con el mensaje.

Ejercicio 5:(2) Cree una clase denominada l)og con un mtodo sobrecargado bark( ) (mtodo "ladrar). Este mtodo

debe estar sobrecargado basndose en diversos tipos de datos primitivos y debe imprimir diferentes tipos de ladridos, gruidos, etc., dependiendo de la versin sobrecargada que se invoque. Escriba un mtodo main ) que invoque todas las distintas versiones

Ejercicio 6:(I) Modifique el ejercicio anterior de modo que dos de los mtodos sobrecargados tengan dos argumen

5 imcializacion y limpieza 270 tos (de dos tipos distintos), pero en orden inverso uno respecto del otro. Verifique que estas definiciones funcionan.

Ejercicio 7:(I) Cree una clase sin ningn constructor > luego cree un objeto de esa clase en niain( ) para verificar que

se sintetiza automticamente el constructor predeterminado. La palabra clave this

Si tenemos do> objetos del mismo tipo llamados a y b podemos preguntamos cmo es posible invocar un mtodo peelt ) para ambos objetos [nata, la palabra inglesa pee! significa "pelar una fruta, que en este ejemplo de programac 011 es una banana):

inicializatlon/BananaFeel.java [ / ... *'/ I

class Sanana | void peel i int 11 public class BananaPeel |

pblic static vold mam (String ti argsl { Banana * - new Banana (), b = new Banana M;
a. b.

peel il) ; peel (2) ;

5 imcializacion y limpieza 271 )

} ///.-

Si slo hay un nico mtodo denominado pt*el( ). cmo puede ese mtodo saber si est siendo llamado para el objeto a o para el objeto b '

Para poder escribir el cdigo en una sintaxis cmoda orientada a objetos, en la que podamos enviar un mensaje a un objeto. el compilador se encarga de realizar un cierto trabajo entre bastidores por nosotros. Existe un primer argumento secreto pasado al mtodo peel( ). y ese argumento es la referencia al objeto que se est manipulando. De este modo, las dos llamadas a mtodos se convierten en algo parecido a: Banana. peel (a, 1) ,* Banana.peel(b, 2);

Esto se realiza internamente y nosotros no podemos escribir estas expresiones y hacer que el compilador las acepte, pero este ejemplo nos basta para hacemos una idea de lo que sucede en la practica.

5 imcializacion y limpieza 272 Suponga que nos encontramos dentro de un mtodo >queremos obtener lareferencia al objeto actual Puesto que el compi

lador pasa esa referencia secretamente, no existe ningn identificador para ella. Sin embargo, y para poder acceder a esa referencia, el lenguaje incluye una palabra clave especifica: this La palabra clave this. que slo se puede emplear en mtodos que no sean de tipo static devuelve la referencia al objeto para el cual ha sido invocado el mtodo. Podemos tratar esta referencia del mismo modo que cualquier otra referencia a un objeto. Recuerde que, si est invocando un mtodo de la clase desde dentro de otro mtodo de esa misma clase, no es necesario utilizar this. sino que simplemente basta con invocar el mtodo La referencia this actual ser utilizada automticamente para el otro mtodo. De este modo, podemos escribir: //: initialization/Apricot.j ava public class Apricot ( void pick() ( /* , , * #/ J void pitO { pickO; /* ... */ )

} ///;-

Dentro de pit( ), podramos decir t!tis.pick( ) pero no hay ninguna necesidad de hacerlo. 13 El compilador se encarga de hacerlo automticamente por nosotros. La palabra clave this slo se usa en aquellos casos especiales en los que es necesario utilizar explcitamente la referencia al objeto actual. Por ejemplo, a Alguna* personas escriben obsesivamente thU leanle de cada llamada a mtodo * referencia a un campo argumentando que eso hace que el ctSdigo >ea miU claro y ms explcito" Mi consejo es que no lo haga. l-.Mste una razn por la que utilizamos los lenguajes de alto nivel, y esa razn es que estos lenguajes \c encargan de hacer buena parte del trabajo por no>otro>. Si incluimos la palabra clave lili cuando no e> necesario, las personas que lean el cdigo se sentirn confundidas, ya que los dcm> programas que hayan ledo en cualquier parte no utilizan las palabra clave this de manera contmuu L i programadores esperan que Ihl* slo se use all donde sea necesario Hl seguir un estilo de codificacin cohcrvutc y simple permite ahorrar tiempo y dinero.
13

5 imcializacion y limpieza 273 menudo se usa en instrucciones return cuando se quiere devolver la referencia al objeto actual: //: initialization/Leaf.java // Uso simple de la palabra clave thisMM. public class Leaf ( int i - 0 / Leaf mcrement \) (

-M-; return this,*

i void prmt () { System.out.println{**i * + i);

) public static void main(String[1 args) ( Leaf x = new Leaf () x.incrementI>.increment ).increment().printl)j

) ) / Output: i * 3

5 imcializacion y limpieza 274 *///:-

Puesto que incremente) devuelve la referencia al objeto actual a travs de la palabra clave this, pueden realizarse fcilmente mltiples operaciones con un mismo objeto.

La palabra clave this tambin resulta til para pasar el objeto actual a otro mtodo nitializtion/PaaingThis.java class Person ( public void eat(Apple apple' ( Apple peeled = apple.getPeeied1 > , System.out.crincin Yurarcy"1 ;

jlass Peeler |

5 imcializacion y limpieza 275 sratic Apple peel Apple applej ( / i ... pelar

return apple; // Pelada

class Apple {

Asele oecPesledi) { return Peeler.pee!<this); \

5 imcializacion y limpieza 276 punlic class PassingThis ( publlc static void mainlStringlI args { r.ew Per son () . eac (new Apple (l 1;

} ) /* Outpuc:

Vummy

//":-

El objeto Apple (manzana) necesita invocar Peeler.peel( ). que es un mtodo de utilidad externo que lleva a cabo una operacin que. por alguna razn, necesita ser externa a Apple (quiza ese mtodo externo pueda aplicarse a muchas clases distintas y no queremos repetir el cdigo). Para que el objeto pueda pasarse a si mismo al mtodo extemo, es necesario emplear this

5 imcializacion y limpieza 277 Ejercicio 8: el) Cree una clase con dos mtodos Dentro del primer mtodo invoque al segundo mtodo dos veces: la

primera vez sin utilizar this > la segunda utilizando dicha palabra cla\e. Realice este ejemplo simplemente para ver cmo funciona el mecanismo, no debe utilizar esia forma de invocar a los mtodos en la prctica. Invocacin de constructores desde otros constructores

C liando se escriben varios constructores para una clase, existen ocasiones en las que conviene invocar a un constructor desde dentro de otro para no tener que duplicar el cdigo. Podemos efectuar este tipo de llamadas utilizando la palabra clave this

Normalmente, cuando escribimos this. es en el sentido de "este objeto" o el "objeto actual. > esa palabra clave genera, por si misma, la referencia al objeto acnial. Dentro de un constructor, la palabra clave this toma un significado distinto cuando se la proporciona una lista de argumentos: realiza una llamada explcita al constructor que se corresponda con esa lista de argumentos. De este modo, disponemos de una forma sencilla de invocar a (ros constructores:

mtial izat ion/Flower. java Llamada a constructores con this, import static net.mindview.til.Print.;

5 imcializacion y limpieza 278 public class Flower f mt petalCount * 0;

String s = "initial valu";

Flowerlint petis ( petalCount = petis;

printl"Constructor w/ int arg only, petalCount "

petalCount);

Flower(String ss) { print ("Constructor w/ String arg only, s * H t- ss) ; S a SS;

} Flower{String s, int petis) ( this(petis) ; //! this(s); // jNo podemos realizar dos invocaciones! this.s = s; // Otro uso de "this" print("String i int aros");

5 imcializacion y limpieza 279 ) Flower{) { this("hi", 47); print("default constructor (no args)");

i void printPetalCount() ( //! this11); // No dentro de un no-constructor! print("petalCount = + petalCount - " s = " s);

} public static void main(String[] args) { Flower x new Plowerl); x.printPetalCount(};

} ) / * Output: Constructor w/ int arg only, petalCount* 47 String L int args default constructor (no args) petalCount = 47 s = hi *///:-

5 imcializacion y limpieza 280 Ul constructor Flo\ver(String s. int petis) muestra que, aunque podemos invocar un constructor utilizando this. no podemos invocar dos Adems, la llamada al constructor debe ser lo primero que hagamos, porque de lo contrario obtendremos un mensaje de error de compilacin.

Este ejemplo tambin muestra otro modo de utilizacin de this. Puesto que el nombre del argumento s y el nombre del miembro de datos s son iguales, existe una ambigedad. Podemos resolverla utilizando this.s. para dejar claro que estamos refirindonos al miembro de datos. F.sta forma de utilizacin resulta muy habitual en el cdigo Java y se emplea en numerosos lugares del libro.

En printPetalCount( ) podemos ver que el compilador no nos permite invocar un constructor desde dentro de cualquier mtodo que no sea un constructor.

Ejercicio 9: (1) Cree una clase con dos constructores (sobrecargados). Utilizando this. invoque el segundo constructor

desde dentro del primero.

5 imcializacion y limpieza 281 El significado de static

l'eniendo en mente el significado de la palabra clave this. podemos comprender mejor qu es lo que implica definir un mtodo como static. Significa que no existir ningn objeto this para ese mtodo concreto. No se pueden invocar mtodos no static desde dentro de los mtodos static: (aunque la inversa si es posible), y se puede invocar un mtodo static para la propia clase, sin especificar ningn objeto. De hecho, esa es la principal aplicacin de los mtodos static: es como si estuviramos creando el equivalente de un mtodo global. Sin embargo, los mtodos globales no estn permitidos en Java, y el incluir el mtodo static dentro de una clase permite a los objetos de esa clase acceder a mtodos static \ a campos de tipo static.

Algunas personas argumentan que los mtodos estticos no son orientados a objetos, ya que tienen la semntica de un mtodo global Un mtodo esttico no enva un mensaje a un objeto, ya que no existe referencia this. Probablemente se trate de

5 imcializacion y limpieza 282 : ti nico cuso en que esto puede hacerse es cuando se pasa a) mtodo static una referencia .t un objeto (el mtodo stutic tambin podra crear su propio objeto) Entonces, a travs de la referencia ique ahora ser, en la prctica, this). se pueden invocar mtodos no static y acceder a campos no itatie. Pero, normalmente s queremos hacer algo como cito, lo mejor e< que escribamos un mtodo no static normal y corriente un argumento correcto, y si se encuentra alguna vez utilizando una gran cantidad de mtodos estticos probablemente convenga que \ uelva a meditar sobre la estrategia que est empleando Sin embargo. ION mtodos y valores estticos resultan bstanle prcticos. y hay ocasiones en las que de verdad son necesarios, por lo que la cuestin de si se trata de verdadera programacin orientada a objetos es mejor dejrsela a los tericos Limpieza: finalizacin y depuracin de memoria

Los programadores son conscientes de la importancia de la inieializacin, pero a menudo se olvidan de que tambin la limpieza es importante. Despus de todo, quin necesita limpiar un valor int' Pero, con las bibliotecas, limitarse a olvidarse de un objeto despus de haber acabado de utilizarlo no siempre resulta seguro. Por supuesto. Jav a dispone del dcpuiador de memoria para reclamar la memoria ocupada por aquellos obietos que va no estn siendo utilizados. Pero pensemos en lo que sucede en algunos casos poco usuales suponga que el objeto que lia definido asigna memoria especial" sin utilizar new. El depurador de memoria slo sabe como liberar la memoria asignada con new. por lo que no sabra como liberar la memoria "especian del objeto Para estos casos lava proporciona un mtodo denominado finalize( ) que se puede definir paru la clase. He aqu como se supone que funciona ese mtodo: cuando el depurador de memoria est listo para liberar el almacenamiento utilizado por el objeto, invocar primero finalizet ) \ slo reclamar la memoria del objeto en la siguiente pasada del depurador de memoria Por tanto, si decidimos utilizar finalizet ). tendremos la posibilidad de realizar (areas de limpieza importantes en el mntenlo en que se ptoJuzi o la depuracin Je memoria.

Esta es mui posible fuente de error de programacin, porque algunos programadores, especialmente los que provienen del campo del C+. pueden confundirse inicialmente V considerar que finalizet ) es el destructor da C-t. que es una funcin que se invoca siempre cuando >e destruye un objeto F * importante entender la distincin que existe enire C * y Java a este respecto, porque en O*. los objetos siempre se destruyen ten un programa libre de errores, mientras que en Java, el depurador de memoria no siempre procesa los objetos Dicho de otro modo I Fue Je que el JepuraJoi Je memoria no procese los objetos 2. I a depuracin Je memoria nn es equivalente a ta destrucc in Je/ objeto.

5 imcializacion y limpieza 283 Si m,* recuerdan estos dos principios, se podran evitar muchos problemas. Lo que quieren decir es que. si existe alguna actividad que deba de ser realizada antes de que un objeto deje de ser necesario, deberemos realizar dicha actividad nosotros nmmos Java no dispone de mtodos destructores ni de ningn otro concepto similar, por lo que es necesario crear un mtodo normal para llevar a cabo esta tarea de limpieza. Por ejemplo, suponga que. en el proceso de creacin de un objeto, ese objeto se dibuja a si mismo en la pantalla. Si no borramos explcitamente su imagen de la pantalla, puede que esa imagen nunca llegue a borrarse Si incluimos una cierta funcionalidad de borrado dentro de finalizet ). entonces si un objeto se ve sometido al proceso de depuracin de memoria y se invoca finalizet I (y recordemos que no existe ninguna garanta de que esto suceda), entonces se eliminar primero la imagen de la pantalla: pero si no incluimos explcitamente esa funcionalidad de borrado, esa imagen permanecer.

IMdotnov encontrarnos con la situacin de que nunca llegue a liberarse el espacio de almacenamiento de un objeto, debido a que el programa nunca se acerca a un punto en el que exista un nesgo de quedarse sin espacio de almacenamiento. Si el programa <e completa v el depurador de memoria no llega a entrar en accin para eliminar el espacio de almacenamiento asignado a los objetos, dicho espacio ser devuelto al sistema operativo en masut en el momento de salir del programa Lsta caracterstica resulta bastante conveniente, porque el proceso de depuracin de memoria implica un cierto gasto adicional de recursos de procesamiento, y si no se lleva a cabo, el casto no se produce. Para qu se utiliza finalize()?

Per. entonces, si no debe utilizarse finalizet > como mtodo de limpieza de propsito general, para qu sirve este mtodo '

n tercer punto que hay que recordar es: 3. La depuracin Je memoria solo se preocupa Je la memoria.

5 imcializacion y limpieza 284 s decir, la nica razn para la existencia del depurador de memoria es recuperar aquella memoria que el programa ya no esta utilizando Por tanto, cualquier actividad asociada con la depuracin de memoria, y en especial el mtodo finalizet ). debe tambin encargarse nicamente de la memoria y de su desasignacin .Significa esto que. si el objeto contiene otros objetos, finalize( ) debe liberar explcitamente esos otros objetos? En realidad no el depurador de memoria s ocupa de liberar toda la memoria de objetos, independientemente de cmo estos hayan sido creados, tn resumen, finalizet ) solo es necesario en aquellos casos especiales en los que el objeto pueda asignar espacio de almacenamiento utilizando alguna tcnica distinta de la propia creacin de objetos. Algn lector especialmente atento podra argumentar: pero, si todo Java es un objeto. ,.como puede llegar a producirse esta situacin?
I

Parece que finalze() se ha incluido en el lenguaje debido a la posibilidad de que el programador realice alguna aeti\ idad de estilo C. asignando memoria mediante algn mecanismo distinto del nonnalmenre empleado en Java. Esto puede suceder. principalmente, a travs de los mtodos nativos, que son una forma de invocar desde Jav a cdigo escrito en un lenguaje distinto de Java (los mtodos nativos se estudian en el Apndice B de la segunda edicin electrnica de este libro, disponible en www.Mhutt'icu.net). C > C'-t son los nicos lenguajes actualmente soportados por los mtodos nativos, pero como desde ellos se pueden invoeai subprogramas en otros lenguajes, en la prctica podremos invocar cualquier cosa que queramos Dentro del cdigo no Java, podra invocarse la familia de funciones malloc( ) de C para asignar espacio de almacenamiento. y a menos que invoquemos free( ). dicho espacio de almacenamiento no ser liberado, provocando una fuga de memoria. Por supuesto. free( ) es una funcin de C \ C por lo que sera necesario invocarla mediante un mtodo nativo dentro de flnalize( )

Despus de estas explicaciones, el lector probablemente estar pensando que no va a tener que utilizar finalizet ) de forma demasiado frecuente 1 En efecto, cn asi dicho mtodo nn es el lugar apropiado para realizar las tareas normales de limpieza Pero, entonces dnde lleva a cabo esas tareas normales de limpieza?

5 imcializacion y limpieza 285 Es necesario efectuar las tareas de limpieza

Para limpiai 1111 objeto, el usuario de ese objeto debe invocar un mtodo de limpieza en el lugar donde desee que sta se realice Esto parece bastante sencillo, pero choca un poco con el concepto C de destructor. En Cf--. todos los objetos se destruyen: o. mejor dicho, todos los objetos deberan destruirse Si el objeto C * * se crea como local (es decir, en la pila, lo cual no resulta posible en Java), la destruccin se produce en la llave de cierre del mbito en que el objeto haya sido creado Si el objeto se cre utilizando new (como en Java), el destructor se invoca cuando el programador llama al operador Cdetele tque no existe en Java. Si el programador de O+ olvida invocar delote, nunca se llamar al destructor y se producir en la prctica una fuga de memoria (adems de que las otras partes del objeto nunca llegarn a limpiarse). Este tipo de error puede ser muy difcil de localizar y es una de las principales razones para pasar de O* a Java

Por contraste. Java no permite crear objetos locales, sino que siempre es necesario utilizar new Pero en Java, no existe ningn operador delete" para liberar el objeto, porqu el depurador de memoria se encarga de liberar el espacio de almacenamiento por nosotros. Por tanto, desde un punto de vista simplista, podramos decir que debido a la depuracin de memoria Java no dispone de destructores. Sin embargo, a medida que avancemos en el libro veremos que la presencia de un depurador de memoria no elimina ni la necesidad ni la utilidad de los destructores <y recuerde que no se debe invocar finalizo! 1 directamente, por l< que dicho mtodo no constituye una solucin). Si queremos que se realice algn tipo de limpieza distinta de la propia liberacin del espacio de almacenamiento, signe siendo necesario invocar explcitamente un mtodo apropiado en Java, que ser el equivalente del destructor de C pero sin la comodidad rociada a ste.

Recuerde que no esian garantizadas 111 la depuracin de memoria 111 la finalizacin Si la maquina \ iriual Java (JVM) no esta prxima a quedarse sin memoria, puede que no pierda tiempo recuperando espacio mediante el mecanismo de depuracin de memoria.

5 imcializacion y limpieza 286 La condicin de terminacin

En general, no podemos confuir en que finaliztM ) sea invocado v es necesario crear mtodos de limpieza separado* e invocarlos explcitamente Por tanto, parece que finalize< ) slo resulta til para oscuras tareas de limpieza de memoria que la mayora de los programadores nunca van a tener que utilizar. Sin embargo, existe un caso interesante de uso de tinalize( > que no depende de que dicha funcin sea invocada iodas las veces Nos referimos a la verificacin de la condicin de terminal ion de un objeto. losliua Bloch is tontuna na. ujutitc cu su seccin "IV/ii A HnoU:mhivs" "U> Imuliltcs imjswkvibkS a menudo peligro** v generalmente inuecoarios' AVAi/iu Lntiium/n GuUU |> 20 t Aldfcxn*NVc*!c>. 2ihi| 1 L11 linntu itcaibiio pi rtil Veuner , m h t irwmt i %m 1 ti im >cminjm> ju impartinw* ctniuimainente

En el momento en que ya no estemos interesados en un objeto (cuando est listo para ser borrado) dicho objeto deber encontrarse en un estado en el que su memoria debe ser liberada sin riesgo. Por ejemplo, si el objeto representa un archivo abierto, el programador deber cerrar dicho archivo antes de que el objeto se vea sometido al proceso de depuracin de memoria. Si alguna parte del objeto no se limpia apropiadamente, tendremos un error en el programa que ser muy difcil de localizar Podemos utilizar finalizet ) para descubrir esta condicin, incluso aunque dicho mtodo no sea siempre invocado. Si una de las finalizaciones nos permite delectar el error, habremos descubierto el problema, que es lo nico que realmente nos importa.

He aqu un ejemplo simple de cmo podra emplearse dicho mtodo: //: imtialization/TerminationCondit ion. java // Oso de finalizet) para detectar un objeto // que no ha sido limpiado apropiadamente.

5 imcializacion y limpieza 287 class Book ( boolean checkedOut = false; Bookboolean checkOut) { checkedOut = checkOut;

} void checkln() ( checkedOut false;

1 protected void finalizet) { if(checkedOut) System.out.printlnl"Error: checked out"); // Normalmente, tambin haremos esto: l // super.finalize0; // Invocar la versin de la clase base

i public class TerminationCondition ( public static void main(String[] args) | Book novel = new Book(tru); t Limpieza apropiada: novel.checkln(J;

5 imcializacion y limpieza 288 // Falta la referencia, nos olvidamos de limpiar, new Book(trut ; // Forzar la depuracin de memoria y la finalizacin:

Syelem.gc)r

) ) /* Output: Error: checked out V//3-

La condicin de terminacin es que se supone que todos los objetos Book (libro) deben ser devueltos (chvck in) antes de que los procese el depurador de memoria, pero en maint ) hay un error de programacin por el que uno de los libros no es devuelto. Sin finali/e( ) para verificar la condicin de terminacin, este error puede ser difcil de localizar.

Observe que se utiliza System.jc< ) para forzar la finalizacin. Pero, incluso aunque no usramos esc mtodo, resulta altamente probable que llegramos a descubrir el objeto Book errneo ejecutando repetidamente el programa (suponiendo que el programa asigne un espacio de almacenamiento suficiente como para provocar la ejecucin del depurador de memona).

5 imcializacion y limpieza 289 Por regla general, debemos asumir que la versin de finalizet ) de la clase base tambin estar llevando a cabo alguna tarea importante, por lo que convendr invocarla utilizando super. como puede verse en Book.finalize( ) En este caso, hemos desactivado esa llamada mediante comentarios, porque requiere utilizar los mecanismos de tratamiento de excepciones de los que an no hemos hablado en detalle.

Ejercicio 10: (2)Creeunaclaseconun mtodo finalizet ) que imprima un mensaje. En maint ). cree un objeto de esa

clase. Explique el comportamiento del programa.

Ejercicio 11: (4)Modifique finalizet ).

el ejercicio

anterior de modo que siempre se invoque el mtodo

Ejercicio 12: (4) Cree una clase denominada Tank (tanque) que pueda ser llenado y vaciado, y cuya condicin de ter

5 imcializacion y limpieza 290 minacin es que el objeto debe estar vacio en el momento de limpiarlo. Escriba un mtodo finalizo! ) que verifique esta condicin de terminacin. En main( ). compruebe los posibles casos que pueden producirse al utilizar los objetos lank Cmo funciona un depurador de memoria

Si su experiencia anterior es con lenguajes de programacin en los que asignar espacio de almacenamiento a los objetos en el cmulo de memoria resulta muy caro en temimos de recursos de procesamiento, puede que piense que el mecanismo Java de asignar todo el espacio de almacenamiento (excepto para las primitivas) en el cmulo de memoria es tambin caro. Sin embargo, resulta que el mecanismo de depuracin de memoria puede contribuir significativamente a acelerar la velocidad de creacin de los objetos. Esto puede parecer un poco extrao a primera vista (el que la liberacin del espacio de almacenamiento afecte a la velocidad de asignacin de dicho espacio) pero sa es la forma en que funcionan algunas mquinas JVM. lo que implica que la asignacin de espacio de almacenamiento en el cmulo de memoria para los objetos Java puede ser casi tan rpida como crear espacio de almacenamiento en la pila en otros lenguajes.

Por ejemplo, podemos pensar en el cmulo de memoria de C++ como si fuera una parcela de terreno en la que cada objeto ocupa su propio lote de espacio. Este terreno puede quedar abandonado y debe ser reutilizado. En algunas JVM el cmulo de memoria Java es bastante distinto: se parece ms a una cinta transportadora que se desplaza hacia adelante cada vez que se asigna un nuevo objeto. Esto quiere decir que la asignacin de espacio de almacenamiento a los objetos es notablemente rpida: simplemente se desplaza hacia adelante el puntero del cmulo de memoria para que apunte a un espacio vacio, por lo que equivale en la prctica a la asignacin de espacio de almacenamiento en la pila en C *-+ (por supuesto, existe un cierto gasto adicional de recursos de procesamiento asociado a las tareas de administracin del espacio, pero esos recursos son mnimos comparados con los necesarios para localizar espacio de almacenamiento).

Algn lector podra argumentar que el cmulo de memoria no puede considerarse como una cima transportadora, y que si lo consideramos de esa manera comenzarn a entrar en accin los mecanismos

5 imcializacion y limpieza 291 de paginacin de memoria, desplazando informacin hacia y desde el disco, de modo que puede parecer que disponemos de ms memoria de la que realmente existe. Los mecanismos de paginacin afectan significativamente a la velocidad. Adems, despus de crear un nmero grande de objetos terminar por agotarse la memoria. El truco radica en que el depurador de memoria, mientras se encarga de liberar el espacio de almacenamiento que ya no es necesario, compacta tambin todos los objetos en el cmulo de memoria, con lo que el efecto es que el puntero del cmulo de memoria queda situado ms cerca del comienza de esa cinta transportadora". ms alejado del punto en el que pueda producirse un fallo de pgina. El depurador de memoria se encarga de reor- denar la informacin y hace posible utilizar esc modelo de cmulo de memoria infinito de alta velocidad para asignar el espacio de almacenamiento.

Para entender el proceso de depuracin de memoria en Java, resulta til analizar cmo funcionan los esquemas de depuracin de memoria en otros sistemas. Una tcnica muy simple, pero muy lenta, de depuracin de memoria es la que se denomina recuento tic referencias Esto quiere decir que cada objeto contiene un contador de referencias y que, cada vez que se asocia una referencia a ese objeto, ese contador de referencias se incrementa. De la misma forma, cada vez que una referencia se sale de mbito o se la asigna el valor nuil, se reduce el contador de referencias. Este mecanismos de gestin del nmero de referencias representa un gasto adicional pequeo, pero constante, que tiene lugar mientras dura la ejecucin del progiama. El depurador de memoria recorre la lista completa de objetos, y donde encuentra uno cuyo contador de referencias sea cero, libera el espacio de almacenamiento correspondiente (sin embargo, los mecanismos basados en el recuento de referencias suelen liberar los objetos tan pronto como el contador pasa a \alcr cero), La desventaja es que. si hay una serie de objetos que se refieren circula! mente entre si. podemos encontramos con nmeros de referencia distintos de cero a pesar de que los objetos ya no sean necesarios. La localizacin de esos grupos auto-referenciales exige al depurador de memoria realizar un trabajo adicional bastante significativo. Este mecanismo de recuento de referencias se suele utilizar de forma bastante habitual para explicar uno de los posibles mecanismos de depuracin de memoria, pero no parece que se use en ninguna implementacin de mquina JVM.

En otros esquemas ms rpidos, la depuracin de memoria no est basada en el recuento del nmero de referencia, en su lugar, se basa en el concepto de que cualquier objeto que no est muerto debe, en ltimo trmino, ser trazable hasta otra referencia que est localizada en la pila o en almacenamiento esttico. Esta cadena debe atravesar varios niveles de objetos. De este modo, si comenzamos en la pila y en el rea de almacenamiento esttico y vamos recorriendo todas las referencias, podremos localizar todos los objetos vivos. Para cada referencia que encontremos, debemos continuar coa el proceso de traza, entrando en el objeto al que apunta la referencia y siguiendo a continuacin todas las referencias incluidas en ese objeto. entrando en los objetos a los que esas referencias apuntan, etc., hasta recorrer todo el rbol que se origina en la referencia >ituada en la pila o en almacenamiento esttico. Cada objeto a travs del cual pasemos seguir estando viv. Observe que no se presenta el problema de los grupos auto-referenciales: los objetos de esos grupos no sern recorridos durante este proceso de construccin

5 imcializacion y limpieza 292 del rbol, por lo que se puede deducir automticamente que hay que depurarlos.

En la tcnica que acabamos de describir, la JVM utiliza un esquema de depuracin de memoria adaptativo. en el que lo que hace con los objetos vivos que localice depender de la variante del esquema que se est utilizando actualmerte. Una de esas variantes es parar y copiar, lo que quiere decir que (por razones que luego comentaremos) se detiene primero el programa (es decir, no se trata de un esquema de depuracin de memoria que funcione en segundo plano). Entonces cada objeto vivo se copia desde un punto del cmulo de memoria a otro, dejando detrs todos los objetos muertos. Adems, a medida que se copien los objetos en la nueva zona del cmulo de memoria, se los empaqueta de modo que ocupen un espacio de almacenamiento mnimo compactando asi el rea ocupada (y permitiendo que se asigne nuevo espacio de almacenamiento inmediatamente a continuacin del rea recin descrita, como antes hemos comentado).

por supuesto, cuando se desplaza un objeto de un sitio a otro, es preciso modificar todas las referencias que apuntan al objeto. Las referencias que apunten al objeto desde el cmulo de memoria o el rea de almacenamiento esttico pueden modificarle directamente, pero puede que haya otras referencias apuntando a este objeto que sean encontradas posteriormente, durante el proceso de construccin del rbol. Estas referencias se irn modificando a medida que sean encontradas (imagine. por ejemplo, que se utilzala una tabla para establecer la correspondencia entre las antiguas direcciones y las nuevas)

I lay

dos problemas que hacen que estos denominados depuradores copiadores* sean poco eficientes. El primero es la necesidad de disponer de dos reas de cmulo de memoria, para poder mover las secciones de memoria entre una y otra, lo que eri la prctica significa que hace falta el doble de memoria de la necesaria. Algunas mquinas JVM resuelven este problema asignando el cmulo de memoria de segmento en segmento, segn sea necesario, y simplemente copiando de un segmento a otro.

5 imcializacion y limpieza 293 El segundo problema es el propio proceso de copia. Una vez que el programa se estabilice, despus de iniciada la ejecucin, puede que no genere ningn objeto muerto o que genere muy pocos. A pesar de eso. el depurador copiador seguir copiando toda la memoria de un sitio a otro, lo que constituye un desperdicio de recursos. Para evitar esto, algunas mquinas JVM detectan que no se estn generando nuevos objetos muertos y conmutan a un esquema distinto (sta es la parte "adaptati- va"). Este otro esquema se denomina manar y eliminar, y es el que utilizaban de manera continua las anteriores versiones de la JVM de Sun. Para uso general, esta tcnica de marcar y eliminar es demasiado lenta, pero resulta, sin embargo, muy rpida cuando sabemos de antemano que no se estn generando objetos muertos.

La tcnica de marcar y eliminar sigue la misma lgica de comenzar a partir de la pila y del almacenamiento esttico y trazar todas las referencias para encontrar los objetos vivos. Sin embargo, cada vez que se encuentra un objeto vivo, ste se marca activando un indicador contenido en el mismo, pero sin aplicarle ningn mecanismos de depuracin. Slo cuando el proceso de marcado ha terminado se produce la limpieza. Durante esa fase, se libera la memoria asignada a los objetos muer- ios Sin embargo, hay que observar que no se produce ningn proceso de copia, por lo que si el depurador de memoria decide compactar un cumulo de memoria fragmentado, tendr que hacerlo moviendo los objetos de un sitio a otro.

El concepto de parar y copiar hace referencia a la idea de que este tipo de depuracin de memoria no se hace en segundo plano; en lugar de ello, se detiene el programa mientras tiene lugar la depuracin de memoria. En la documentacin tcnica de Sun podr encontrar muchas referencias al mecanismo de depuracin de memoria donde se dice que se trota de un proceso de segundo plano de baja prioridad, pero la realidad es que la depuracin de memoria no estaba implementada de esa forma en las primeras mquinas JVM de Sun. En lugar de ello, el depurador de memoria de Sun detena el programa cuando detectaba que haba poca memoria libre. La tcnica de marcar y limpiar tambin requiere que se detenga el programa.

Como hemos mencionado anteriormente, en la mquina JVM descrita aqu, la memoria se asigna en bloques de gran tamao. Si asignamos un objeto grande, ste obtendr su propio bloque. La tcnica de detencin y copiado estricta requiere que se copien todos los objetos vivos desde el cmulo de memoria

5 imcializacion y limpieza 294 de origen hasta un nuevo cmulo de memoria antes de poder liberar el primero, lo que implica una gran cantidad de memoria. Utilizando bloques, el mecanismo de depuracin de memoria puede normalmente copiar los objetos a los bloques muertos a medida que los va depurando. Cada bloque dispone de un contador de generacin para ver si est vivo En el caso normal, slo se compactan los bloques creados desde la ltima pasada de depuracin de memoria; para todos los demas bloques se incrementar el contador de generacin si han sido referen- ciados desde algn sitio. Esto permite gestionar el caso normal en el que se dispone de un gran nmero de objetos temporales de corta duracin. Peridicamente, se hace una limpieza completa, en la que los objetos de gran tamao seguirn sin ser copiados (simplemente se incrementara su contador de generacin) y los bloques que contengan objetos pequeos se copiarn y compactarn La maquina JVM moni (oriza la eficiencia del depurador de memoria y. si este mecanismo se convierte en una perdida de tiempo porque todos los objetos son de larga duracin, conmuta al mecanismo de marcado y limpieza. De forma similar, la JVM controla hasta qu punto es efectiva la tcnica de marcado y limpieza, y si el cmulo de memoria comienza a estar fragmentado, conmuta al mecanismo de detencin y copiado. Aqui es donde entra en accin la parte *adap- tativa del mecanismo, al que podramos describir de manera rimbombante como : mecanismo adaptativo generacional de detencin-copiado y marcado-limpieza

Existen varias posibles optimizaciones de la velocidad de una mquina JVM. Una especialmente importante afecta a la operacin del cargador y es lo que se denomina compilador. fust-in-time (JIT). Un compilador JIT convierte parcial o totalmente un programa a cdigo mquina nativo, de modo que dicho cdigo no necesite ser interpretado por la JVM. con lo que se ejecutar mucho ms rpido. C uando debe cargarse una clase (normalmente, la primera vez que queramos crear un objeto en esa clase) se localiza el archivo .class y se carga en memoria el cdigo intermedio correspondiente a dicha clase. En este punto, una posible tcnica consiste en compilar simplemente todo el cdigo, para generar un cdigo mquina, pero esto tiene dos desventajas: necesita algo ms de tiempo, lo cual (si tenemos en cuenta toda la vida del programa) puede representar una gran cantidad de recursos adicionales; e incrementa el tamao del ejecutable (el cdigo intermedio es bastante ms compacto que el cdigo JIT expandido), y esto puede provocar la aparicin del fenomeno de paginacin, lo que ralentiza enormemente los programas. Otra tcnica alternativa es la evaluacin lenta, que consiste en que el cdigo intermedio no se compila para generar cdigo mquina hasta el momento necesario. De este modo, puede que nunca se llegue a compilar el cdigo que nunca llegue a ejecutarse. Las tecnologas HotSpot de Java en los kits de desarrollo JDK recientes adoptan una tcnica similar, optimizando de manera mcrcmcntal un fragmento de cdigo cada vez que se ejecuta, por lo que cuanto ms veces se ejecute, ms rpido lo har. Inicializacin de miembros

Java adopta medidas especiales para garantizar que las variables se inicial icen adecuadamente antes de

5 imcializacion y limpieza 295 usarlas. En el caso de las variables locales de un mtodo esta garanta se presenta en la forma de errores en tiempo de compilacin. Por tanto, si escribimos:

void t () ( int i; i++; // Error i no inicial izada

obtendremos un mensaje de error que dice que puede no haber sido no micializada. Por supuesto, el compilador podra haber dado a i un valor predeterminado, pero el hecho de que exista una variable local 110 inicializada es un error del programador. y el valor predeterminado estara encubriendo ese error. Forzando al programador a proporcionar un valor de inicializacin. es ms probable que se detecte el error

Sin embargo, si hay un campo de tipo primitivo en una clase, las cosas son algo distintas. Como hemos visto en el Capitulo

5 imcializacion y limpieza 296 Toda es un objeto, se garantiza que cada campo primitivo de una clase contendr un valor inicial He aqui un programa que permite verificar este hecho y mostrar los valores: //; initialixation/InitialValuea.java
2,

// Muestra los valores iniciales predeterminados. import static net .mindview.util. Pnnt. * ; public class InitialValues { boclean t; char c ; byte b; short s; int i; long 1; float f; double dr InitialValues reference; void printlnitialValues(} (

) public static void main(String[] args) { Ini tiaivalues iv = new Initial Values () ,* iv.printlnitialValues0; i * Tambin podramos escribir: new InitialValues().printlnitial Values();

Puede ver que, an cuando no se Kan especificado los valores, los campos se inicializan automticamente (el valor correspondiente a citar es cero, lo que se imprime como un espacio). De modo que, al menos, no existe ningn nesgo e llegar a trabajar con variables no inicial izadas.

Cuando definimos una referencia a objeto dentro de una clase sin inicializarse como nuevo objeto, dicha referencia se int- csaliza con el valor especial nuil

5 imcializacion y limpieza 297 Especificacin de la inicializacin

Que sucede si queremos dar un valor inicial a una variable? Una forma directa de hacerlo consiste, simplemente, en asignar el valor en el punto en que definamos la variable dentro de la clase (observe que no se puede hacer esto en C++. aunque los programadores novatos de C-H* siempre lo intentan), En el siguiente fragmento de cdigo, se modifican las definiciones de los campos de la clase InitialYalues para proporcionar los correspondientes valores iniciales: //: inltialization/InitialValues2.java // Definicin de valores iniciales explcitos. public class IntialValues2 1 boolean bool = true; char ch = *x' byte b * 4 7 ; ahort s * Oxff; int i = 9 99; long lng =. 1 ; float f = 3 .14f , double d = 3.14159;

) ///:-

Tambin podemos imcializar objetos no primitivos de la misma forma. Si Depth es una clase, podemos crear una \ariable e inicialtzaria como sigue: /: irutialization/Measuremen t. java class Depth {) public class Measurement ( Deoth d = new Depth O ;

5 imcializacion y limpieza 298 // ...

} ///:-

Si no hubiramos dado a d un valor inicial y tratramos de usarlo de todos modos, obtendramos un error de tiempo de ejecucin denominado excepcin (hablaremos de este tema en el Capitulo 12. Tratamiento de errores mediante excepciones).

Podemos incluso llamar a un mtodo para proporcionar un valor de inicializacin: //: intializaton/Methodlnit .java public class Methodlnit { nt i f ( \ ; int f() { return 11; )

} ///:-

5 imcializacion y limpieza 299 Por supuesto, este mtodo puede tener argumentos, pero dichos argumentos no pueden ser otros miembros de la clase que todava no hayan sido inicializados. Por tanto, podemos hacer esto: //: initialization/MethodXnit 2.java public class MethodInlt2 { int i - f ( ) ; int j * g(lj / int f O { return 11; | i n t gint n) { return r* *10; (

) ///:-

Pero no esto: //: initiali zaton/Method!nit3.java public class MethodInlt3 ( //! int j= g(i)j // Referencia anticipada ilegal

) ///:-

5 imcializacion y limpieza 300 Este es uno de los ejemplos en los que el compilador se queja, como es lgico, acerca de las referencias anticipadas, ya que este caso tiene que ver con el orden de inicializacin ms que con la torma en que se compila el programa.

Esta tcnica de inicializacin resulta bastante simple y directa. Tiene la limitacin de que tados los objetos de tipo InitialValues contendrn el mismo valor de inicializacin. En ocasiones, esto es. exactamente, lo que queremos, pero en otros casos hace falta ms flexibilidad. Inicializacin mediante constructores

Podemos emplear el constructor para realizar la inicializacin, y esto nos proporciona una mayor flexibilidad a la hora de programar, porque podemos invocar mtodos y realizar acciones en tiempo de ejecucin para determinar los valores iniciales. Sin embargo, es preciso tener presente una cosa: esto no excluye la inicializacinautomtica que tiene lugar antes de

entrar en el constructor. Asi que. si escribimos por ejemplo: //: initialization/Counter.ja va public class Counter ( int i; Counter(> { i * 7; } // . . .

5 imcializacion y limpieza 301 } ///:-

entonces i se inicializar primero con el valor 0. y luego con el valor 7. Esto es cierto para todos los tipos primitivas v tambin para las referencias a objetos, incluyendo aquellos a los que se inicialice de manera explcita en el punto en el que se los defina. Por esta razn, el compilador no trata de obligamos a inicializar los elementos dentro del constructor en ningn sitio concreto o antes de utilizarlos: la inicializacin ya est garantizada. Orden de inicializacin

Dentro de una clase, el orden de inicializacin se determina mediante el orden en que se definen las variables en la clase. Las definiciones de variables pueden estar dispersas a travs de y entre las definiciones de mtodos, pero las variables se inicializan antes de que se pueda invocar cualquier mtodo, incluso el constructor. Por ejemplo: //: initialisation/OrderGflnitializat ion. java t t Ilustra el orden de inicializacin. import stat-ic net.mindview.util.Prmt. Cuando se invoca el constructor para crear un // objeto Window, aparecer el mensaje: clase Window { Window<int marker) ( print I "Window < *' + marker * }

} class House ( Window wl = new Window( l ) // Antes del constructor House 1 |

5 imcializacion y limpieza 302 // Mostrar que estamos en el constructor: print("House O"); w3 = new Window1331; t f Reinlcallzar w3

) Window w2 <= new Window{2) ; // Despus del constructor voidf) { print{"ft)"); } Window w3 = new Window(3), // Al final i public class OrderOfInitialization { public static void mainlStringU argst ( House h = new House ();
h.

ftJi // Muestra que la construccin ha finalizado

) } /* Output: Window(1) Window(2) Window(3) House(J Window(33I f O *///?-

En House. las definiciones de los objetos Window han sido dispersadas intencionadamente, para demostrar que todos ellos se inicializan antes de entrar en el constructor o de que suceda cualquier otra cosa. Adems. w3 se reinicializa dentro del constructor.

5 imcializacion y limpieza 303 Examinando la salida, podemos ver que la referencia a w3 se inicial iza dos veces. Una vez antes y otra durante la llamada al constructor (el primer objeto ser eliminado, por lo que podr ser procesado por el depurador de memoria ms adelante). Puede que esto no le parezca eficiente a primera vista, pero garantiza una inicializacin adecuada; qu sucedera si se definiera un constructor sobrecargado que no inicializara w3 y no hubiera una inicializacin predeterminada* para 3 en su definicin? Inicializacin de datos estticos

Slo existe una nica rea de almacenamiento para un dato de tipo static, independientemente del nmero de objetos que se creen. No se puede aplicar la palabra clave static a las variables locales, as que slo se aplica a los campos. Si un campo es una primitiva de tipo static y no se inicializa, obtendr el valor inicial estndar correspondiente a su tipo. Si se trata de una referencia a un objeto, el valor predeterminado de inicializacin ser nuil.

Si desea colocar la inicializacin en el punto de la definicin, ser similar al caso de las variables no estticas.

Para ver cundo se inicializa el almacenamiento de tipo static, ha aqui un ejemplo: //t lntalization/StaticInitialization.java // Especificacin de valores iniciales en una definicin de clase. import static net.mindview.util.Print.; class Bowl { Bowl(int marker) { print(MBowl(" marker H)w);

5 imcializacion y limpieza 304 I void f1(int marker) ( print("fl(" + marker

) class Table ( static Bowl bowll = new Bowl(II; TableO ( print ("Table 0*1; bowl2,f1(1)?

) void f2(int marker) { print I "f2 (" + marker * "J");

) static Bowl bowl2 = new Bowl(2);

5 imcializacion y limpieza 305 ) class Cupboard { Bowl bowl3 new Bowl(3); static Bowl bowl4 = new Bowl(4); Cupboard() ( print("Cupboard O H J; bowl4.f1 (2 ); I void f3(int marker) { print("f3(" marker ")")j

) static Bowl bowls new Bowl(5);

} public class Staticlnitialization ( public static void mam (String 1] args) { print("Creating new CupboardO in main*); new Cupboard(); print("Creating new CupboardO in main"); new Cupboardl); table.f2 1); cupboard.f3(1}; l static Table table new TableO; static Cupboard cupboard = new Cupboard 0 I /* Oucpuc:

5 imcializacion y limpieza 306 Bowl(1) Bowl<2) Table() fUl) Bowl(4) Bowl(5) Bowl(3) Cupboard() f 1 (2) Creacing new Cupboard(i in main Bowl(3) Cupboard() f 1 (2) Creating new Cupboard() in main 3owl(3) '.upboard (} 1(2) 2(1) f 3 {1)

*///:-

Bowl pcmnte visualizar la creacin de una clase, mientras que Table y Cupboard tienen miembros de tipo static de Bowl dispersos por sus correspondientes definiciones de clase. Observe que Cupboard crea un objeto Bowl bow*L3 no esttico antes de las definiciones de tipo static.

5 imcializacion y limpieza 307 Examinando la salida, podemos ver que la inicializacin de static slo tiene lugar en caso necesario. Si no se crea un objeto Table y nunca se hace referencia a Table.bowll o Table.bowl2. los objetos Bowl estticos bowll y bowl2 nunca se crearn. Slo se inicializarn cuando se cree el primer objeto Table (cuando tenga lugar el primer acceso static) Despus de eso. los objetos static no se remicializan.

El orden de inicializacin es el siguiente: primero se inicializan los objetos estticos, si es que no han sido ya imcializados con una previa creacin de objeto, y luego se inicializan los objetos no estticos. Podemos ver que esto es asi examinando la salida del programa. Para examinar main( ) (un mtodo static), debe cargarse la clase Staticlnitialization. despus de lo cual se inicializan sus campos estticos table y cupboard, lo que hace que esas clases se carguen y. como ambas contienen objetos Bowl estticos, eso hace que se cargue la clase Bowl. Por tanto, todas las clases de este programa concreto se cargan antes de que d comienzo main( ). ste no es el caso usual, porque en los programas tpicos no tendremos todo s incu- lado entre si a travs de valores estticos, como sucede en este ejemplo.

Para resumir el proceso de creacin de un objeto, considere una clase Dog:

1.

Aunque no utilice explcitamente la palabra clave static. el constructor es. en la prctica, un mtodo static. Por tanto, la primera vez que se crea un objeto de tipo Dog. o la primera v ez que se accede a un mtodo esttico o a un campo esttico de la clase Dog, el intrprete de Java debe localizar Dog.class. para lo cual analiza ruta de clases que en ese momento haya definido (classpath).

2.

A medida que se carga Dog.class (creando un objeto Class. acerca del cual hablaremos

5 imcializacion y limpieza 308 posteriormente) se ejecutan todos sus inicializadorcs de tipo static. De este modo, la inicializacin de tipo static slo tiene lugar una vez, cuando se carga por primera vez el objeto Class

3.

Cuando se crea un nuevo objeto con new Dog( ), el proceso Do

de construccin asignaprimero

del objeto el

suficiente espacio de almacenamiento para el objeto Dog en el cmulo de memoria.

4.

Este espacio de almacenamiento se rellena con ceros, lo que asigna automticamente sus valores predeterminados a todas las primitivas del objeto Dog (cero a los nmeros y el equivalente para boolean v char); asimismo,

este proceso hace que las referencias queden con el valor nuil

5 imcializacion y limpieza 309


5.

Se ejecutan las inicializaciones especificadas en el lugar en el que se definan los campos.

6.

Se ejecutan los constructores. Como podremos ver en el Capitulo 7. Reutilizacin de las clases. esto puede implicar un gran nmero de activ idades, especialmente cuando estn implicados los mecanismos de herencia.

Inicializacin static explcita

Java permite agrupar otras i nidal izae iones estticas dentro de una clusula" static especial (en ocasiones denominada bloque esttica) en una clase. F.l aspecto de esta clusula es el siguiente: //: initiali2 ation/Spoon.java public class Spoon ( static inc i; static ( i = 47*

) ///*.-

5 imcializacion y limpieza 310 Parece ser un mtodo, pero se trata slo de la palabra clave static seguida de un bloque de cdigo. Este cdigo, al igual que otras inicializacinnes estticas slo se ejecuta una vez: la primera vez que se crea un objeto de esa clase o la primera vez que se accede a un miembro de tipo static de esa clase (incluso aunque nunca se cree un objeto de dicha clase). Por ejemplo: //: initialization/ExplicitStatic.java // Inicializacin static explcita con la clusula "static", import static net.mindview.util.Print.*; class Cup { Cup I int nvarker) ( print < "Cup ( marker * )**);

! void (int marker) { print <**f(" t- marker ")'*>; I

} class Cups { static Cup cupl; static Cup cup2; static ( cupl = new Cup(l) ; cup2 - new Cup(2);

5 imcializacion y limpieza 311 ) Cups(J ( print ('CupsO H);

) I public class ExplicrtStatic | public static void main(String[J args ( print ("InGide main ()"),* Cups.cupl.f(99);// (1)

} // static Cups cupsl * new CupsO,* // 12) // static Cups cups2 * new CupsO; // (2) } / Output: Inside mainO Cup(1) Cup(2)
I

(99)

///:-

5 imcializacion y limpieza 312 Los inicializadores static para Cups se ejecutan cuando tiene lugar el acceso del objeto esttico cupl en la linea marcada con (I), osi se desactiva mediante un comentario la linea (I) y se quitan los comentarios que desactivan las lineas marcadas (2). Sise desactivan mediante comentarios tanto (I) como (2), la inicializacin static deCups nunca tiene lugar,como puede verse a la salida. Asimismo, da igual si se eliminan las marcas de comentario que estn desactivando a una y otra de las linea* marcadas (2) o si se eliminan las marcas de ambas lneas; la inicializacin esttica tiene lugar una sola vez.

Ejercicio 13: (I) Verifique las afirmaciones contenidas en el prrafo anterior.

Ejercicio 14: (!) Cree una clase con un campo esttico String que sea inicializado en el punto de definicin, y otro

campo que se inicialice mediante el bloque static. Aada un mtodo static que imprima ambos campos y demuestre que ambos se inicializan antes de usarlos. Inicializacin de instancias no estticas

Java proporciona una sintaxis similar, denominada inicializacin Je instancia, para inicializar las variables estticas de cada objeto. He aqui un ejemplo: ) i : initialization/Mugs.java // Inicializacin de instancia" en Java, import static

5 imcializacion y limpieza 313 net.mindview.util.Print. * ; class Mug { Mug{int marker) { print("Mug<" marker + ")") ; l void f(int marker) { print (Mf(N *- marker "));

) I public class Mugs { Muq mugl; Mug mug2;

{ mugl * new Mug(l); mug2 = new Mug(2); print("mugl 4 mug2 initialized");

) Mugs() ( print("Mugs()"J;

5 imcializacion y limpieza 314 ) Mugs t i nr. i) { print("Mugs(int) w) ;

I public static void main(String{ ] args' { print{uInside main 0"); new Mugs(); print ("new Mugs!) completed**) ? new Mugs(l); print(Wnew Mugs(l) completed');

) ) /* Output: Inside main() Mug(1) Mug(2) mugl & mug2 initialized Mugs () new KugsO completed Mug (1) Mug(2) mugl & mug2 initialized Mugs(int)

Podemos ver que la clusula de inicial i/acin de instancia: 315 Piensa en Java new Mugs 11) completed *///:- mug * new Mug|1); mug2 new Mug(21; print"mug & mug2 initialized");

parece exactamente como la clusula de inicializacin estatica. salvo porque falta la palabra clave static. Esta sintaxis es necesaria para soportar la inicializacin de clases internas annimas (vase el Captulo 10. Clases internas), pero tambin nos permite garantizar que ciertas operaciones tendrn lugar independientemente de qu constructor explcito se invoque. Examinando la salida, podemos ver que la clusula de inicializacin de instancia se ejecuta antes de los dos constructores.

Ejercicio 15: (1) Cree una clase con un campo String que se inicialice mediante una clusula de inicializacin de ins

tancia.

Podemos ver que la clusula de inicial i/acin de instancia: 316 Piensa en Java Inicializacin de matrices

Una matriz es, simplemente, una secuencia de objetos o primitivas que son todos del mismo tipo y que se empaquetan juntos. utilizando un nico nombre identificador. Las matrices se definen y usan mediante el aperador de indexacin | | Para definir una referencia de una matriz, basta con incluir unos corchetes vacos detrs del nombre del tipo: int [J al;

Tambin puede colocar los corchetes despus del identificador para obtener exactamente el mismo resultado: int al [] ;

Esto concuerda con las expectativas de los programadores de C y C++. Sin embargo, el primero de los dos estilos es una

sintaxis ms adecuada, ya que comunica mejor que el tipo que estamos definiendo es una matriz de variablesde tipo int

Podemos ver que la clusula de inicial i/acin de instancia: 317 Piensa en Java Este estilo es el que emplearemos en el libro.

El compilador no permite especificar el tamao de la matriz. Esto nos retrotrae al problema de las referencias anteriormente comentado. Todo lo que tenemos en este punto es una referencia a una matriz (habiendo asignado el suficiente espacio de almacenamiento para esa referencia), sin que se haya asignado ningn espacio para el propio objeto matriz. Para crear espacio de almacenamiento para la matriz, es necesario escribir una expresin de inicializacin. Para las matrices, la inicializacin puede hacerse en cualquier lugar del cdigo, pero tambin podemos utilizar una clase especial de expresin de inicializacin que slo puede emplearse en el punto donde se cree la matriz. Esta inicializacin especial es un conjunto de valores encerrados entre llaves. En este caso, el compilador se ocupa de la asignacin ilc espacio (el equivalente de utilizar new) Por ejemplo: int I] al - { 1. 2, 3, 4. 5 );

Pero entonces, por qu bamos a definir una referencia a una matriz sin definir la propia matriz? int [3 a2;

Bueno, la razn para definir una referencia sin definir la matriz asociada es que en Java es posible asignar una matriz a otra, por lo que podramos escribir: a2 = al;

Lo que estamos haciendo con esto es, en realidad, copiar una referencia, como se ilustra a continuacin:

Podemos ver que la clusula de inicial i/acin de instancia: 318 Piensa en Java //: initialization/ArraysOfPrimitive s.java mport static net.mindview.til.Print; public class ArraysOfPrimitives ( public static void mainString[] args) { int [] al * { l t 2, 3, 4, 5 }; int [] a2; a2 * al; forlint i = 0; i < a2.1ength; a2 [ij = a2[ij 1; for(int 1=0; 1 < al.length; i++) prmt(Mal[M i + **J H al til);

) ) /* Output: al(01 * 2 al IU = 3 al(2j * 4 al 13] = 5 al [4] * 6 *///:-

Como puede ver. a al se le da un valor de inicializacin. pero a a2 no: a a2 se le asigna posteriormente un valor que en este caso es la referencia a otra matriz. Puesto que a2 y al apuntan ambas a la misma matriz, los cambios que se realicen a travs de a2 podrn verse en al

Todas las matrices tienen un miembro intrnseco (independientemente de si son matrices de objetos o

Podemos ver que la clusula de inicial i/acin de instancia: 319 Piensa en Java matrices ce primitivas) que puede consultarse (aunque no modificarse) para determinar cuntos miembros hay en la matriz. Este miembro es length. Puesto que las matrices en Java, al igual que en C y C++, comienzan a contar a partir del elemento cero, el elemento mximo que se puede indexar es lengtb - 1. Si nos salimos de los lmites, C y C++ lo aceptarn en silencio y permitirn que hagamos lo que queramos en la memoria, lo cual es el origen de muchos errores graves. Sin embargo. Java nos protege de tales problemas provocando un error de tiempo de ejecucin (una excepcin) si nos salimos de los limites.5

Qu sucede si no sabemos cuntos elementos vamos a necesitar en la matriz en el momento de escribir el programa? Simplemente, bastar con utilizar new para crear los elementos de la matriz. Aqu, new funciona incluso aunque se est creando una matriz de primitivas (sin embargo, new no permite crear una primitiva simple que no forme paite de una matriz): //: initialization/ArrayNew.j ava // Creacin de matrices con new. import java.ucil; import static r.et .mindview.util. Print. *; public class ArrayNew ( public static void mainString[] args) { intll a; Random rand = new Random(47) a = new int (rand.nextInt (20)) pnnt ("length of a = " + a.length); print (Arrays. toString (a)) ;

} / Output: length of a s 18 [0, 0, 0, 0, 0, 0. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0. 0, 01 V//:-

F.l tamao de la matriz se selecciona aleatoriamente utilizando el mtodo Random.ne\tlnt(), que genera un valor entre cero y el que se le pase como argumento. Debido a la aleatoriedad. est claro que la creacin de la matriz tiene lugar en tiempo de ejecucin. Adems, como la salida del programa muestra que los elementos de matriz de tipos primitivos se inicializan automticamente con valores vacos (para las variables numricas y char. se inicializan con cero mientras que para variables boolean se

Podemos ver que la clusula de inicial i/acin de instancia: 320 Piensa en Java inicializan con false).

F.l mtodo Arrays.ioString( ). que forma parte de la biblioteca estndar java.util. genera una versin imprimible de una matriz unidimensional. ' Por supuesto, comprobar cada acceso de una nuitri/ cuesta tiempo y cdigo, y no hay manera de desactivar esas comprobaciones, lo que quiere decir que los accesos a matrices pueden ser una fuente de ineticiencia eu los programa, si se producen en alguna seccin critica. En arus de la seguridad en Interne! ' de la productividad de los programadores, los diseadores ai Java pensaron que resultaba conveniente pagar este precio para evitar los errores asociados con las matrices Aunque el programador pueda sentirse tentado de escribir cdigo para tratar de hacer que los accesos a las matrices sean ms eficientes. esto es una perdida de tiempo, porque las optimizaciones automticas en tiempo de compilacin y en tiempo de ejecucin se encargan de acelerar los accesos a las matrices.

Por supuesto, en este caso la matriz tambin poda haber sido definida e inieializada en la misma instruccin: intlj a ** new int [rand.nextlnt (20 ) ] ;

sta es la forma preferible de hacerlo, siempre que se pueda.

Si se crea una matriz que no es de tipo primitivo, lo que se crea es una matriz de referencias. Considere el tipo envoltorio Integer, que es una clase y no una primitiva:

Podemos ver que la clusula de inicial i/acin de instancia: 321 Piensa en Java //; initialization/ArrayClassObj.java // Creacin de una matriz de objetos no primitivos. lmport java.til.* import static net.mindview.util.Print.* ; public class ArrayClassGbj ( public static void main(String[] argsl { Random rand * new Random(47) Integerf] a new Integer [rand.nextlnt(20) 1 ; print("length of a * H + a.iength); forint 1 = 0 ; i < a.iength; i++) a[i] = rand.nextlnt1500) ; // Conversin automtica print{Arrays.toString(a)};

) /* Output:(Sample) length of a = 18 [55, 193, 361, 461. 429, 368, 200, 22. 207, 288, 128, 51, 89, 309, 278, 498, 361, 20] *///:-

Aqui. incluso despus de invocar new para crear la matriz: Integerfl a <= new Integer[rand.nexLInt(20)];

es slo una matriz de referencias y la micializacin no se completa hasta que se inicialice la propia referencia creando un nuevo objeto Integer (mediante el mecanismo de conversin automtica, en este caso): ali] = rand.nextlnt(500);

Podemos ver que la clusula de inicial i/acin de instancia: 322 Piensa en Java Sin embargo, si nos olvidamos de crear un objeto, obtendremos una excepcin en tiempo de ejecucin cuendo tratemos de utilizar esa posicin vacia de la matriz.

Tambin es posible inicializar matrices de objetos mediante una lista encerrada entre llaves. He aqui dos formas de hacerlo:

//: initializationyArraylnit.java // Inical2acin de la matriz, import j ava.uti1.; public class Arraylnit { public static void main(StringU args) ( IntegerU a * ( new Integerti), new Integer12), 3, // Conversin automtica IntegerU b = new IntegerU ( new Integer(1), new Integer(2), 3, // Conversin automtica

); System.out.printiniArrays.toString\a>i; System.out.println:Arrays.toString(b));

Podemos ver que la clusula de inicial i/acin de instancia: 323 Piensa en Java ) ) / Output: 11. 2. 3] ti. 2, 31 *///:-

\n ambos casos, la coma final de la lisia de inicializadores es opcional (esta caracterstica permite un mantenimiento mas fcil de las listas de gran tamao).

Aunque la primera forma es til, es ms limitada, porque slo puede emplearse en el punto donde se define la matriz. Podemos utilizar las formas segunda y tercera en cualquier lugar, incluso dentro de una llamada a un mtodo. Por ejemplo, podramos crear una matriz de objetos String para pasarla a otro mtodo main( ). con el fin de proporcionar argumentos de linea de comandos alternativos a ese mtodo main( ): //: initialization/DynamcArray. java i ) Inicializacin de la matriz. public clase DynamcArray { public static void main(StringU aros) { Other.mainnew String[]{ "fiddle", "de", "dum" \ ) ;

Podemos ver que la clusula de inicial i/acin de instancia: 324 Piensa en Java } class Other ( public static void main(StringH args) { for(String s : args) System.out .print (s *- "

} ) /* Output: fiddle de dura *///:-

La matriz creada para el argumento de Other.main( ) se crea en el punto correspondiente a la llamada al mtodo, asi que podemos incluso proporcionar argumentos alternativos en el momento de la llamada

Ejercicio 16: (1) Cree una matriz de objetos Strinj y asigne un objeto String a cada elemento. Imprima la matriz utili

zando un bucle for.

Podemos ver que la clusula de inicial i/acin de instancia: 325 Piensa en Java Ejercicio 17: (2) Creeunaclaseconunconstructorquetome un argumento String. Durante la construccin, imprima

el argumento. Cree una matriz de referencias a objetos de esta clase, pero sin crear ningn objeto para asignarlo a la matriz. Cuando ejecute el programa, observe si se imprimen los mensajes de inicializacin correspondientes a las llamadas al constructor.

Ejercicio 18: (l) Complete el ejercicio anterior creando objetos que asociar a la matriz de referencias. Listas variables de argumentos

i segunda forma proporciona una sintaxis cmoda para crear c invocar mtodos que pueden producir un efecto similar a las lisias variables de argumentos de C (conocidas con el nombre de "varargs" en C). Esto puede incluir uu nmero desconocido de argumentos que a su vez pueden ser de tipos desconocidos. Puesto que todas las clases se heredan en ltima instancia de la clase raz comn Object (tema del que hablaremos ms adelante en el libro), podemos crear un mtodo que admite una matriz de Object e invocarlo del siguiente modo: //: imtialization/VarArgs.java // ffso de la sintaxis de matriz para crear listas variables de argumentos, class A () public class VarArga { static void printArray(Object(] argsl { for(Object obj : args) System.out .print (obj -t- " *) ;

Podemos ver que la clusula de inicial i/acin de instancia: 326 Piensa en Java System.out.println();

327 Piensa en Java public static void main(String[] args) ( printArray(new Object[1 (new Integer(47), new Float(3.l4), new Double(11.11) 1; printArray (new Object 1] ("one" , MtwoM, "three }); printArray(new Object U (new A(), new A(), new A()));

) ) / Output: (Sample) 47 3.14 11.11 one two three A&la46e30 A<>3e25a5 A&19821f *///:-

Podemos ver que print( ) admite una matriz de tipo Object. y recorre la matriz utilizando la sintaxis foreach imprimiendo cada objeto. Las clases de la biblioteca estndar de Java generan una salida ms comprensible, pero los objetos de las clases que hemos creado aqui imprimen el nombre de la clase, seguido de un signo de y de una serie de dgitos hexadeci- malcs. Por tanto, el comportamiento predeterminado (si no se define un mtodo toString( ) para la clasc. como veremos posteriormente en el libro) consiste en imprimir el nombre de la clase y la direccin del objeto.

Es posible que se encuentre con cdigo anterior a Java SE5 escrito como el anterior para generar listas variables de argumentos. Sin embargo, en Java SE5, esta caracterstica largo tiempo demandada ha sido finalmente aadida, por lo que ahora podemos emplear puntos suspensivos para definir una lista variable de argumentos, como puede ver en printArray( ): //: nitialization/NewVarArgs.java // Uso de la sintaxis de matrices para crear listas variables de argumentos. public class NewVarArgs (

328 Piensa en Java static void printArray(Object... args) { for(Object obj : args) System.out.print(obj + " ) System.out .printlnO ;

public static void roain(String args) { // Admite elementos individuales: printArray(new Integer(47), new Float(3.14), new Double(11.11)); printArray(47, 3.14F. 11.11); pr intArray (" one ", " two", " three ") ; printArray (new A(), new A(), new AO); // O una matriz: printArrayMObject(])new Integer [] | 1 . 2, 3, 4 }>; printArray 0; // Se admite una lista vacia

) ) /* Output: (75% match) 47 3.14 11.11 47 3.14 11.11 one two three Albab50a Ac3c749 A*150bd4d 12 3 4 ///:-

Con varargs. ya no es necesario escribir explcitamente la sintaxis de la matriz: el compilador se encargar de completarla automticamente cuando especifiquemos varatgs, Seguimos obteniendo una matriz, lo cual es la razn deque print( ) siga pudiendo utilizar la sintaxis foreach para iterar a travs de la matriz. Sin embargo, se trata de algo ms que una simple conversin automtica entre una lista de elementos y una matriz. Observe la penltima linea del programa, en !a que una matriz de elementos Integer (creados con la caracterstica de conversin automtica) se proyecta sobre una matriz Object (para evitar que el compilador genere una advertencia) y se pasa a printArray ). Obviamente, el compilador determina que esto es ya una matriz, por lo que no realiza ninguna conversin con ella. De

329 Piensa en Java modo que, si tenemos un grupo de elementos, podemos pasarlos como una lista, y si ya tenemos una matriz, se aceptar esa matriz como lista variable de argumentos.

La ultima lnea del programa muestra que es posible pasar cero argumentos a una lista vararg. Esto resulta til cuando existen argumentos finales opcionales: //: initialization/OptionalTrailingArguments.java public class OptionalTrailingArguments ( static void fdnt required. String... trailing) { System.out.print{"required: " - required + " "); for(String s : trailing) System.out .print (s -f " ) System.out.printlni ) ;

public static void mam (String [) args) ( f(l, "one"); f(2, HtwoM, "three"); f (0) ;

} ) / Output: required: 1 one required: 2 two three required: 0 ///:-

Esto muestra tambin cmo se pueden utilizar varargs con un tipo especificado distinto de Object. Aqu, todos los varargs deben ser objetos String. Se puede utilizar cualquier tipo de argumentos en las listas varargst incluyendo tipos pnmitivos. El siguiente ejemplo tambin muestra que la lista vararg se transforma en una matriz y que, si no hay nada en la lista, se tratar como una matriz de tamao cero. //: mitialization/VararaType.java

330 Piensa en Java public class VarargType ( static void f(Character... args) { System.out.print(args.getClass0 ) ; System.out.println(M length " + args.length); static void gtint... args) { System.out.print(args.getClass()); System.out.println(M length M * args.length); public static void mam (String [ 3 args) ( fCa'); f ) ; g(l); g ) ; System.out.println(Hint[] : " + new int{0J.getClassO} ;

) ) /* Output; class [Ljava.lang.Character; length 1 class [L^ava.lang.Character; length 0 class [I length 1 class [I length 0 int [] : class [I *///:-

El mtodo getClass( ) es parte de Object. y lo analizaremos en detalle en el Capitulo 14. Informacin de tipos. Devuelve la clase de un objeto, y cuando se imprime esa clase, se ve una representacin del tipo de la clase en forma de cadena de caracteres codificada. El carcter inicial indica que se trata de una matriz del tipo situado a continuacin. La T indica una primitiva int; para comprobarlo, hemos creado una matnz de int en la ltima lnea y hemos impreso su tipo. Esto permite comprobar que la utilizacin de varargs no depende de la caracterstica de conversin automtica, sino que utiliza en la prctica los tipos primitivos.

331 Piensa en Java Sin embargo, las listas vararg funcionan perfectamente con la caracterstica de conversin automtica. Por ejemplo: public class AutoboxmgVarargs ( public static void f(Integer... args) ( for(Integer i : args) System.out .print (i + M M System.out .printlnO i public static void main{String[J args) ( f(new Integer(l), new Integer(2)); f<4, 5, 6, 7, 8 , 9); f{10, new Integer(11), 12);

) ) /* OUtpUt:
1

4 5 6 7 8 9 10 11 12 ///:-

Observe que se pueden mezclar los lipos en una misma lisia de argumentos, y que la caracterstica de conversin automtica promociona selectivamente los argumentos int a Integer

Las listas varar# complican el proceso de sobrecarga, aunque ste parezca suficientemente seguro a primera vista:

332 Piensa en Java //: initial2ation/0verloadingVararg3.java public class OverloadingVarargs ( static void f(Character... args) { System.out.pri nti"f i rst"); for(Character c : args) System.out.print(" " + c); System, out.println(J;

} static void f(Integer... args) ( System.out.print("second"); for(Integer i : args) System, out .print (H " * ); System.out.orintln();

} static void fiLong... args) { System.out.orintln("third");

) public static void main (String [J args { fl'a', b. rcM; f (1) ; f(2, 11; f (0) ; f(OL);

333 Piensa en Java //! f () ,* // No se compilar -- ambiguo

} | /* Output: first abe second i second 2 1 second O third *///:-

En cada caso, el compilador est utilizando la caracterstica de conversin automtica para determinar qu mtodo sobrecargado hay que utilizar, e invocar el mtodo que se ajuste de la forma ms especifica.

pero cuando se invoca f( ) sin argumentos, el compilador no tiene forma de saber qu mtodo debe llamar. Aunque este error es comprensible, probablemente sorprenda al programador de programas cliente.

Podemos tratar de resolver el problema aadiendo un argumento no vararg a uno de los mtodos: //:

334 Piensa en Java initialization/OverloadingVarargs 2, java // {CompileTimeError) (Wont compile) public class Overloadmgvarargs2 { static void ffloat i, Character... args) { System.out.println("first");

} static void f(Character... args) ( } System.out.print("second" ) j

public static void main(String[] args) ( m, ' a ) ,* f('a\ 1 b') ; l

) ///:-

El marcador de comentario {CompileTimeError} excluye este archivo del proceso de construccin Ant del libro. Si lo compila a mano podr ser el mensaje de error: referente to f is ambiguous. both method ffjloat.java. tang. Character...) ln Overloading Varargs2 and mcthod fjava.ang. Character...) in Overloadinglarargs2 match

335 Piensa en Java St proporciona a ambos mtodos un argumento no -vararg. funcionar perfectamente: //j initialization/OverloadinaVarargs3.java public class Overloadir.gVarargs3 { static void f(float i, Character... args) ( System.out,println("firstM);

) static void fichar c, Character... args) ( System.out .println(f'second")

} public static void main(StringU args) (

til, 'a')l re*', 'b* > ;

} ) /* Output: Cirst

336 Piensa en Java second

///:-

Generalmente, slo debe utilizarse una lista variable de argumentos en una nica versin de un mtodo sobrecargado. O bien, considere el no utilizar la lista variable de argumentos en absoluto.

Ejercicio 19: (2) Escriba un mtodo que admita una matriz varaig de tipo String. Verifique que puede pasar una lista

separada por comas de objetos String o una matriz String[| a este mtodo.

Ejercicio 20: (I) Cree un mtodo main( ) que utilice varargs en lugar de la sintaxis mainl > normal. Imprima todos los

337 Piensa en Java elementos de la matriz args resultante. Pruebe el mtodo con diversos conjuntos de argumentos de linea de comandos. Tipos enumerados

338 Piensa en Java

Una adicin aparentemente poco importante en Java SF.5 es la palabra clave enum. que nos facilita mucho las cosas cuando necesitamos agrupar y utilizar un conjunto de tipos enumerados. En el pasado, nos veiamos forzados a crear un conjunto de valores enteros constantes, pero estos conjuntos de valores no suelen casar muy bien con los conjuntos que se necesitan definir y son. por tanto, ms arriesgados y difciles de utilizar. Los tipos enumerados representan una necesidad tan comn que C. C++ y diversos otros lenguajes siempre los han tenido. Antes de Java SE5. los programadores de Java estaban obligados a conocer muchos detalles v a tener mucho cuidado si queran emular apropiadamente el efecto de cnum. Ahora. Java dispone tambin de cnum y lo lia implementado de una manera mucho mas completa que la que podemos encontrar en C C++. He aqui un ejemplo simple: //: initialization/Spicmess.java public enum Spiciness { NCT, MILD, MEDIUM, HOT, FLAMING ) m-.-

Esto crea un tipo enumerado denominado Spiciness con cinco valores nominados. Puesto que las instancias de los tipos enumerados son constantes, se suelen escribir en maysculas por convenio (si hay mltiples palabras en un nombre, se separan mediante guiones bajos).

Para utilizar un tipo cnum. creamos una referencia de ese tipo y la asignamos una instancia: //: initialization/SimpleEnumUse.ja va public class SimpleEnumUse { public static void main(StringU args) { Spiciness howHot = Spiciness.MEDIUM; System.out.DrintlnlhowHot);

5 Imetalizacin y limpieza 339

} / Output: MEDIUM

*///:-

El compilador aade automticamente una sene de caractersticas tiles cuando creamos un tipo cnum Por ejemplo, crea un mtodo tString( ) para que podamos visualizar fcilmente el nombre de una instancia enum. y sa es precisamente la forma en que la instruccin de impresin anterior nos ha permitido generar la salida del programa. El compilador tambin crea un mtodo ordinaK ) para indicar el orden de declaracin de una constante enum concreta, y un mtodo static values() que genera una matriz de valores con las constantes enum en el orden en que fueron declaradas: //: initialization/EnumOrder.java public class EnumOrder ( public static void raain(String[] args) { for(Spiciness s : Spiciness.vales()) System.out .println (s + ", ordinal " + s. ordinal O;

} ) /* Output: NOT, ordinal 0 MILD, ordinal 1 MEDIUM, ordinal 2 HOT, ordinal 3 FLAMING, ordinal 4 *///:-

340 Piensa en Java

Aunque los tipos enumerados enum parecen ser un nuevo tipo de datos, esta palabra clave slo provoca que el compilador realice una serie de actividades mientras genera una clase para el tipo enum. por lo que un enum puede tratarse en muchos sentidos como si fuera una clase de cualquier otro tipo. De hecho, los tipos enum son clases y tienen sus propios mtodos.

Una caracterstica especialmente atractiva es la forma en que pueden usarse los tipos enum dentro de las instrucciones switch: //: initialization/Burrito.java public class Burrito ( Spiciness degree; public Burrito(Spiciness degree) ( this.degree = degree;) public void describe O ( System.out.print<"This burrito is switch(degree) { case NOT: System.out.printlnl"not spicy at all-"); break ,* case MILD: case MEDIUM: System.out. pnntlnC'a little hot."); break; case KOT: case FLAMING: default: System.out.println("maybe too hot."),*

public static void main(String[] args) { Burrito plain = new Burrito(Spiciness.NOT), greenChile = new Burrito(Spiciness.MEDIUM),

5 Imetalizacin y limpieza 341

)'

jalapeno = new Burrito(Spiciness.HOT>; plain.describe()? greenChile.describe(); j alapeno.describe(>;

) /* Output: This burrito is not spicy at all. This burrito is a little hot. This burrito is maybe too hot.

*///:-

Puesto que una instruccin switch se emplea para seleccionar dentro de un conjunto limitado de posibilidades, se complementa perfectamente con un tipo enum Observe cmo los nombres enuin indican de una manera mucho ms clara qu es

lo

que pretende hacer el programa.

En general, podemos utilizar un tipo enum como si fuera otra forma de crear un tipo de datos, y limitamos luego a utilizar los resultados. En realidad, eso es lo importante, que no es necesario prestar demasiada atencin a su uso. porque resulta bastante simple. Antes de la introduccin de enum en Java SE5, era necesario realizar un gran esfuerzo para construir un tipo enumerado equivalente que se

342 Piensa en Java

pudiera emplear de forma segura.

Este breve anlisis es suficiente para poder comprender y utilizar los tipos enumerados bsicos, pero examinaremos estos tipos enumerados ms profundamente en el Capitulo 10, Tipos enumerados.

Ejercicio 21: (l) Cree un tipo enunt con los seis tipos de billetes de euro de menor valor. Recorra en bucle los valores

utilizando values( ) e imprima cada valor y su orden correspondiente con urdiial( ).

Ejercicio 22: (2)Escribauna instruccin switch para el tipo enum del ejercicio anterior. En cada case, imprima una des

cripcin de ese billete concreto

5 Imetalizacin y limpieza 343

Resumen

Este aparentemente elaborado mecanismo de inicializacin. el constructor, nos indica la importancia critica que las tareas de inicializacin tienen dentro del lenguaje. Cuando Bjame Stroustrup. el inv entor de C++. estaba diseando ese lenguaje, una de las primeras cosas en las que se fij al analizar la productividad en C fue que la inicializacin inadecuada de las vana- bles es responsable de una parte significativa de los problemas de programacin. Este tipo de errores son difciles de localizar, y lo mismo cabria decir de las tareas de limpieza inapropiadas. Puesto que los constructores nos permiten garantizar una inicializacin y limpieza adecuadas (el compilador no permitir crear un objeto sin las apropiadas llamadas a un constructor), la seguridad y el control estn garantizados.

En C-H-. la destruccin tambin es muy importante, porque los objetos creados con new deben ser destruidos explcitamente. En Java, el depurador de memoria libera automticamente la memoria de los objetos que no son necesarios, por lo que el mtodo de limpieza equivalente en Java no es necesario en muchas ocasiones (pero cuando lo es. es preciso implemen- tarlo explcitamente). En aquellos casos donde no se necesite un comportamiento similar al de los destructores, el mecanismo de depuracin de memoria de Java simplifica enormemente la programacin y mejora tambin en gran medida la seguridad de la gestin de memoria. Algunos depuradores de memoria pueden incluso limpiar otros recursos, como los recursos grficos y los descriptores de archivos Sin embargo, el depurador de memoria hace que se incremente el coste de ejecucin, resultando difcil evaluar adecuadamente ese coste, debido a la lentitud que histricamente han tenido los intrpretes de Java Aunque a lo largo del tiempo se ha mejorado significativamente la velocidad de Java, un problema de la velocidad ha supuesto un obstculo a la hora de adoptar este lenguaje en ciertos tipos de problemas de programacin.

Debido a que est garantizado que todos los objetos se construyan, los constructores son ms complejos de lo que aqu hemos mencionado. En particular, cuando se crean nuevas clases utilizando los mecanismos de composicum o de herencia. tambin se mantiene la garanta de construccin, siendo necesaria una cierta sintaxis adicional para soportar este mecanismo. Hablaremos de la composicin, de la herencia y del efecto que ambos mecanismos tienen en los constructores en prximos captulos.

Puede encontrar las soluciones a los ejercicios seleccionados en el documento electrnico Pie Thtnktng ni Java Annonucd Sohiuon Guale, que estd disponible paru la venta en wn-wAfinttl7nv.net.Control de acceso

El control de acceso (u ocultacin Je la implementation) trata acerca de que no salgan las cosas a la primera.

Todos los buenos escritores, incluyendo aquellos que escriben software, saben que un cierto trabajo no est terminado hasta despus de haber sido reescrito, a menudo muchas veces. Si dejamos un fragmento de codigo encima de la mesa durante un tiempo y luego volvemos a l, lo ms probable es que veamos una forma mucho mejor de escribirlo. sta es una de las principales motivaciones para el trabajo de rediseo, que consiste en reescribir cdigo que va funciona con el fin de hacerlo ms legible, comprensible y, por tanto, mantenible.

Sin embargo, existe una cierta tensin en este deseo de modificar y mejorar el cdigo. A menudo, existen consumidores (j)ro- gramadores de cliente) que dependen de que ciertos aspectos de nuestro cdigo continen siendo iguales. Por tanto, nosotros queremos modificar el cdigo, pero ellos quieren que siga siendo igual. Es por eso que una de las principales consideraciones en el diseo orientado a objetos es la de separar las cosas que cambian de las cosas que permanecen.

listo es particularmente importante para las bibliotecas Los consumidores de una biblioteca deben poder confiar en el elemento que estn utilizando, y saber que no necesitarn reescribir el cdigo si se publica una nueva versin de la biblioteca. Por otro lado, el creador de la biblioteca debe tener la libertad de reali/ar modificaciones y mejoras, con la confianza de que el cdigo del cliente no se ver afectado por esos cambios.

Estos objetivos pueden conseguirse adoptando el convenio adecuado. Por ejemplo, el programador de la biblioteca debe aceptar no eliminar los mtodos existentes a la hora de modificar una clase de la biblioteca, ya que eso hara que dejara de funcionar el cdigo del programador de clientes. Sin embargo, la situacin inversa es un poco ms compleja de resolver. En el caso de un campo, cmo puede saber el creador de la biblioteca a qu campos han accedido los programadores de clientes? Lo mismo cabe decir de los mtodos que slo forman parte de la implementacin de una clase y que no estn para ser usados directamente por el programador de clientes. Qu pasa si el creador de la biblioteca quiere deshacerse de una imple- mentacin anterior y sustituirla por una nueva? Si se modifica alguno de esos miembros, podra dejar de funcionar el cdigo de algn programa cliente. Por tanto, el creador de la biblioteca tiene las manos atadas y no puede modificar nada

Para resolver este problema. Java proporciona especificadores de acceso que permiten al creador de la biblioteca decu que cosas estn disponibles para el programa cliente y qu cosas no lo estn. Los niveles de control de acceso, ordenados de mayor a menor acceso, son public, protected, acceso de paquete (que no tienen una palabra clave asociada) y private. Leyendo el prrafo anterior, podramos pensar que. como diseadores de bibliotecas, conviene mantener todas las cosas lo ms privadas posible y exponer slo aquellos mtodos que queramos que el programa cliente utilice. Esto es cierto, aunque a menudo resulta antinatural para aquellas personas acostumbradas a programar en otros lenguajes (especialmente C) y que estn acostumbradas a acceder a todo sin ninguna restriccin. Cuando lleguen al final del capitulo, estas personas estarn convencidas de la utilidad de los controles de acceso en Java

Sin embargo, el concepto de biblioteca de componentes y el control acerca de quin puede acceder a los componentes de esa biblioteca no es completo. Sigue quedando pendiente la cuestin de cmo

empaqueuir los componentes para formar una unidad de biblioteca cohesionada. Este aspecto se controla mediante la palabra clave package en Java, y los espccifi- cadores de acceso se vern afectados por el hecho de que una clase se encuentra en el mismo paquete o en otro paquete dis tinto. Por tanto, para comenzar este captulo, veamos primero cmo se incluyen componentes de biblioteca en los paquetes Con eso. seremos capaces de entender completamente el significado de los especifieadores de acceso.

package: la unidad de biblioteca

Un paquete contiene un grupo de clases, organizadas conjuntamente dentro de un mismo espado de nombres.

Por ejemplo, existe una biblioteca de utilidad que forma parte de la distribucin estndar de Java, organizada bajo el espacio de nombres java.util. Una de las clases de java.til se denomina ArrayList. Una forma de utilizar un objeto ArrayList consiste en especificar el nombre completo java.til.ArrayList //: access/FullQualification.iava public class FullQualification ( public static void mainlStrmgl] args) ( java.til.ArrayList list = new java.util.ArrayListt;

) ) ///-

Sin embargo, este procedimiento se vuelve rpidamente tedioso, por lo que suele ser ms cmodo

utilizar en su lugar la palabra clave import. Si queremos importar una nica clase, podemos indicar esa clase en la instruccin import //: access/Singlelmport.java import java.util.ArrayList? public class Sinalelmport ( public static void main(StringJ args) { ArrayList lxst = new java.til.ArrayListO ;

) ) Uh-

Ahora podemos usar ArrayList sin ningn cualificador. Sin embargo, no tendremos a nuestra disposicin ninguna de las otras clases de java.util Para importar todas, basta con utilizar tal como hemos visto en los ejemplos del libro. import j ava.til.r

La razn para efectuar estas importaciones es proporcionar un mecanismo para gestionar los espacios de nombres. Los nombres de todos los miembros de las clases estn aislados de las clases restantes. Un mtodo f( ) de la clase A no coincidir con un mtodo ) que tenga la misma signatura en la clase B Pero qu sucede con los nombres de las clases? Suponga que creamos una clase Stack en una mquina que ya disponga de otra clase Stack escrita por alguna otra persona Esta posibilidad de colisin de los nombres es la que hace que sea tan importante disponer de un control completo de los espacios de nombres en Java, para poder crear una combinacin de identtficadores univoca para cada clase

La mayora de los ejemplos que hemos visto hasta ahora en el libro se almacenaban en un nico archivo y haban sido diseados para uso local, por lo que no nos hemos preocupado de los nombres de paquete. Lo cierto es que estos ejemplos si estaban incluidos en paquetes: el paquete predeterminado o innominado Ciertamente, sta es una opcin viable y trataremos de utilizarla siempre que sea posible en el resto del libro, en aras de la simplicidad. Sin embargo, si lo que pretendemos es crear bibliotecas o programas que puedan cooperar con otros programas Java que estn en la misma mquina, deberemos tener en cuenta que hay que evitar las posibles colisiones entre nombres de clases.

Cuando se crea un archivo de cdigo fuente para Java, normalmente se le denomina anidad de compilacin (y tambin, en ocasiones, unidad de traduccin). Cada unidad de compilacin debe tener un nombre que termine en .java, y dentro de la unidad de compilacin puede haber una clase puhlic que debe tener el mismo nombre del archivo (incluyendo el uso de maysculas y minsculas, pero excluyendo la extensin .java del nombre del archivo). Slo puede haber una clase public en cada unidad de compilacin; en caso contrario, el compilador se quejar. Si existen clases adicionales en esa unidad de compilacin, estarn ocultas para el mundo exterior al paquete, porque no son puhlic. y simplemente se tratar de clases ''soporte" para la clase puhlic principal Organizacin dei cdigo

Cuando se compila un archivo java, se obtiene un archivo de salida para cada dase del archivo .java Cada archivo de salida tiene el nombre de una de las clases del archivo .java, pero con la extensin .class. De este modo, podemos llegar a obtener un gran numero de archivos .class a partir de un nmero pequeo de archivos .java. Si el lector ha programado anteriormente en algn lenguaje compilado, estar acostumbrado al hecho de que el compilador genere algn formato intermedio (normalmente un archivo obj") que luego se empaqueta con otros del mismo tipo utilizando un montador (para crear un archivo ejecutable) o un gestor de biblioteca (para crear una biblioteca). sta no es la forma de funcionar de Java. Un programa funcional es un conjunto de archivos .class. que se puede empaquetar y comprimir en un archivo JAR (Java ARrchive). utilizando el archivador jar de Java. El intrprete de Java es responsable de localizar, cargar e interpretar2 estos archivos.

Una biblioteca es un grupo de estos archivos de clase. Cada archivo fuente suele tener una clase public y un nmero arbitrario de clases no pblicas, por lo que no slo existe un componente public para cada archivo fuente. Si queremos especificar que todos estos componentes (cada uno con sus propios archivos .java y .class separados) deben agruparse, podemos utilizar la palabra clave package.

Si usamos una instruccin package. debe aparecer como la primera linea no de comentario en el archivo. Cuando escribimos: package access;

estamos indicando que esta unidad de compilacin es parte de una biblioteca denominada access. Dicho de otro modo, estamos especificando que el nombre de clase pblica situado dentro de esta unidad de compilacin debe integrarse bajo el paraguas correspondiente al nombre access. de modo que cualquiera que quiera usar ese nombre deber especificarlo por completo o utilizar la palabra clave import en combinacin con access. utilizando las opciones que ya hemos mencionado anteriormente (observe que el convenio que se emplea para los nombres de paquetes Java consiste en emplear letras minsculas. incluso para las palabras intermedias).

Por ejemplo, suponga que el nombre de un archivo es MyClass.java. Esto quiere decir que slo puede haber una clase public en dicho archivo y que el nombre de esa clase debe ser MyClass (respetando el uso de maysculas y minsculas): //: access/mypackage/MyClass .java package access.mypackage; public class MyClass (

// ...

>

///t-

Ahora, si alguien quiere utilizar MyClass o cualquiera otra de las clases pblicas de access. deber emplear la palabra clave import para que estn disponibles esos nombres definidos en el paquete access. La alternativa consiste en especificar el nombre completamente cualificado: //: access/QualifedMyClass.java publxc class QuaiifiedMyClass ( public static void mainString{1 args) ( access.mypackage.MyClass m = new access.mypackage.MyClass();

} ///14No hay ninguna caraeienstica de Java que nos obligue a utilizar un interprete Fxisien compiladores Java de cdigo nauvo que generan un nico archivo ejecutable.
14

La palabra clave import permite que este ejemplo tenga un aspecto mucho ms simple. //: access/ImportedMyClass.J ava import acces6.mypackage.*; public class ImportedMyClass ( publc static void mainStringH args) { MyClass TU - new MyClaso () ;

) ///:-

Merece la pena tener presente que lo que las palabras clave package c import nos permiten hacer, como diseadores de bibliotecas, es dividir el espacio de nombres global nico, para que los nombres no colisionen, independientemente de cuantas personas se conecten a Internet y comiencen a escribir clases en Java. Creacin de nombres de paquete unvocos

El lector se habr percatado de que. dado que un paquete nunca estar realmente "empaquetado en un solo

archivo, podr estar compuesto por muchos archivos .class. por lo que el sistema de archivos puede llegar a estar un tanto abarrotado Para evitar el desorden, una medida lgica que podemos tomar sera colocar todos los archivos .class correspondientes a un paquete concreto dentro de un mismo directorio; es decir, aprovechar la estructura de archivos jerrquica del sistema operativo. sta es una de las formas mediante las que Java trata de evitar el problema de la excesiva acumulacin de archivos; veremos esto de otra forma cuando ms adelante presentemos la utilidad jar.

Recopilar los archivos de un paquete dentro de un mismo suhdirectono resuelve tambin otros dos problemas; la creacin de nombres de paquete unvocos y la localizacin de aquellas clases que puedan estar perdidas en algn lugar de la estructura de directorios. Esto se consigue codificando la ruta correspondiente a la ubicacin del archivo .class dentro del nombre del paquete. Por convenio, la primera parte del nombre del paquete es el nombre de dominio Internet invertido del creador de la clase. Dado que est garantizado que los nombre** de dominio Internet sean unvocos. seguimos este convenio nuestro nombre de paquete ser unvoco y nunca se producir una colisin de nombres (es decir, salvo que perdamos el derecho a utilizar el nombre de dominio y la persona que lo comience a utilizar se dedique a escribir cdigo Java con los mismos nombres de ruta que usted utiliz). Por supuesto, si no disponemos de nuestro propio nombre de dominio, deberemos concebir una combinacin que resulte lo suficientemente improbable (como por ejemplo la combinacin de nuestro nombre y apellidos) para crear nombres de paquete unvocos Si ha decidido comenzar a publicar cdigo Java, merece la pena que haga un pequeo esfuerzo para obtener un nombre de dominio.

La segunda parte de esta solucin consiste en establecer la correspondencia entre los nombres de paquete y los directorios de la mquina, de modo que cuando el programa Java se ejecute y necesita cargar el archivo .class. pueda localizar el directorio en el que esc archivo .class resida.

El intrprete Java acta de la forma siguiente. Primero, localiza la variable de entorno CLASSPATH 15 (que se fija a travs del sistema operativo y en ocasiones es definida por el programa de instalacin que instala Java con una herramienta basada en Java en la mquina). CLASSPATH contiene uno o ms directorios que se utilizan como races para buscar los archivos .class Comenzando por esa raiz. el
15

Cumulo nos refiramos a la variable de entumo, utilizaremos letras mayscula* (CLASSPATH).

intrprete toma el nombre de paquete y sustituye cada punto por una barra inclinada para generar un nombre de ruta a partir de la raz CLASSPATH (por lo que el paquete package fno.har.haz se convertira en foo\bar\baz o foo/bar/baz o, posiblemente, en alguna otra cosa, dependiendo del sistema operativo). Esto se concatena a continuacin con las diversas entradas que se encuentren en la variable CLASSPATH. Ser en ese subdirectorio donde el intrprete busque el archivo .class que tenga un nombre que se corresponda con la clase que se est intentando crear (tambin busca en algunos directorios estndar relativos al lugar en el que reside el intrprete Java).

Para comprender esto, considere por ejemplo mi nombre de dominio, que es MindVievv.net. Inviniendo ste y pasndolo a minsculas, net.mindvievv establece mi nombre global univoco para mis clases (antiguamente, las extensiones com. edu. org. etc . estaban en may sculas en los paquetes Java, pero esto se modific en Java 2 para que todo el nombre del paquete estuviera en minsculas) Puedo subdiv idir este espacio de nombres todava ms creando, por ejemplo, una biblioteca denominada simple, por lo que tendr un nombre de paquete que ser: package net .mir.dview.simple;

Ahora, este nombre de paquete puede utilizarse como espacio de nombres paraguas para los siguientes dos archivos: / /: net/mindview/flimple/Vector, java // Creacin de un paquete, package r.et. mindview. simple ; public class Vector { public Vector(} ( System.out.println("net.mindview.simple.Vector");

} ///i-

Como hemos mencionado antes, la instruccin package debe ser la primera linea de no comentario dentro del cdigo del archivo. El segundo archivo tiene un aspecto parecido: //: net/mindview/simpie/List.java // Creacin de un paquete, package net.mindview.simple; public class List ( public List() { System.out.println("net.mindview.simple.List);

} ///:~

Ambos archivos se ubicarn en el siguiente subdirectorio de mi sistema: C:\DOC\JavaT\net\mindview\simple

Observe que la primera linea de comentario en cada archivo del libro ndica la ubicacin del directorio donde se encuentra esc archivo dentro del rbol del cdigo fuente; esto se usa para la herramienta automtica de extraccin de cdigo que he empleado con el libro.

Si examinamos esta ruta, podemos ver el nombre del paquete net.mindview.simple. pero qu pasa con la primera parte de la ruta De esa parte se encarga la variable de entorno CLASSPATH, que en mi mquina es: CLASSPATH. ; D: \ JA VAN LIB; C: \DOC WavaT

Podemos ver que la variable de entorno CLASSPATH puede contener una serie de rutas de bsqueda alternativas.

Sin embargo, existe una variacin cuando se usan archivos JAR. Es necesario poner el nombre real del archivo JAR en la variable de ruta, y no simplemente la ruta donde est ubicado. Asi. para un archivo JAR denominado grape.jar. la variable de ruta incluira: CLASSPATH.;D:\JAVA\LIB;C:\flavors\grape.jar

L na vez que la variable de ruia de busqueda se ha configurado apropiadamente, el siguiente archivo puede ubicarse en cualquier directorio //: access/LibTest.}ava // Utiliza la biblioteca, import

net.mindview.simple. public class LibTest ( public static void main(String[J args) { Vector v = new Vector O; List 1 new List (} ;

i } / Output: net.mindview.simple.Vector net.mindview.simple.List *///:-

Cuando el compilador se encuentra con la instruccin import correspondiente a la biblioteca simple, comienza a explorar todos los directorios especificados por CLASSPATH. en busca del subdirectorio netfmindview/simple. y luego busca los archivos compilados con los nombres apropiados (Vector.class para Vector y List.class para List), Observe que tanto las dos clases como los mtodos deseados de Vector y List tienen que ser de tipo public

La configuracin de CLASSPATH resultaba tan enigmtica para los usuarios de Java inexpertos (al menos lo era para mi cuando comenc con el lenguaje) que Sun ha hecho que en el kit JDK de las versiones ms recientes de Java se comporte de forma algo mas inteligente. Se encontrar, cuando lo instale, que aunque no configure la variable CLASSPATH. podra compilar y ejecutar programas Java bsicos Sin embargo, para compilar y ejecutar el paquete de cdigo fuente de este libro (disponible en uvnt \mJl lewnei), necesitara aadir a la variable CLASSPATH el directorio base del rbol de cdigo.

Ejercicio 1: paquete. Colisiones

(I) Cree una clase dentro de un paquete. Cree una instancia de esa clase fuera de dicho

Qu sucede si se importan dos bibliotecas mediante '** y ambas incluyen los mismos nombres? Por ejemplo, suponga que un programa hace esto: import net.mindvlew.simple./ import java.til;

Puesto que java.til.* tambin contiene una clase Vector, esto provocara una potencial colisin. Sin embargo, mientras que no lleguemos a escribir el cdigo que provoque en efecto la colisin, no pasa nada. Resulta bastante conveniente que esto sea asi. ya que de otro modo nos veramos forzados a escribir un montn de cosas para evitar colisiones que realmente nunca iban a suceder.

La colisin si que se producir si ahora intentamos construir un Vector: Vector v new Vector(); ^A qu clase Vector se refiere esta lnea? I I compilador no puede saberlo, como tampoco puede saberlo el lector. Asi que el compilador generara un error y nos obligar a ser ms explcitos Si queremos utilizar el Vector Java estndar, por ejemplo. deberemos escribir: java.til.Vector v = new java.til.Vector I);

Puesto que esto (junto con la variable CLASSPATH) especifica completamente la ubicacin de la clase Vector deseada, no existir en realidad ninguna necesidad de emplear la instruccin Import Java.til.*, a menos que vayamos a utilizar alguna otra clase definida en java.til

Alternativamente, podemos utilizar la instruccin de importacin de una nica clase para prevenir las colisiones, siempre y cuando no empleemos los dos nombres que entran en colisin dentro de un mismo programa (en cuyo caso, no tendremos ms remedio que especificar completamente los nombres).

Ejercicio 2: I) Tome los fragmentos de cdigo de esta seccin y transfrmelos en un programa para verificar que se

producen las colisiones que hemos mencionado. Una biblioteca personalizada de herramientas

Armados con este conocimiento, ahora podemos crear nuestras propias bibliotecas de herramientas, para reducir o eliminar la escritura de cdigo duplicado Considere, por ejemplo, el alias que hemos estado utilizando para Sy$tem.out.println( ). con el fin de reducir la cantidad de informacin tecleada Ksto puede ser pane de una clase denominada Print. de modo que dispondramos de una instruccin esttica de impresin bastante ms legible //: net/mindview/ut11/Print.java

// Mtodos de impresin que pueden usarse sin // cualificadores, empleando importaciones estticas de Java SE5: package net .raindvew. ut 1 ,* import java.io.*; public class Print { // Imprimir con una nueva linea: public static void print(Object obj) ( ) System.out.printlnlobj)

// Imprimir una nueva linea sola: public static void print<) ( System.out.println(); 1 H Imprimir sin salto de lnea: public static void prmtnb(Object obj) ( System.out.print(ob^ ) : j i f El nuevo printf() de Java SE5 (de C): public static PrmtStream printf(String format, Object... argsI { return System.out.printf(format, args};

) ///:-

Podemos utilizar estas abreviaturas de impresin para imprimir cualquier cosa, bien con la insercin de una nueva linea iprini( )) o sin una nueva linea (printnh( )).

Como habr adivinado, este archivo deber estar ubicado en un directorio que comience en una de las ubicaciones definidas en CLASSPATH y que luego contine con nct/mindvien Despus de compilar, los mtodos static print( ) y printnb( ) pueden emplearse en cualquier lugar del sistema utilizando una instruccin import static: //: access/PrintTest.java Usa los mtodos estticos de impresin de Print.java, import static net.mindview.util.Print.;
II

public class PrintTest ( public static void main(String[] args) | print("Available from now oni"); print (100) ,* print(100L); print(3.14159) ; I ) / Output: Available from now onl 100 100 3.14159

///-

Un segundo componente de esta biblioteca pueden ser los mtodos range( )t que hemos presentado en el Capitulo 4, Control Je la ejecucin, y que permiten el uso de la sintaxis forvach para secuencias simples de enteros:

/: net/mindview/util/Range.java // Mtodos de creacin de matrices que se pueden usar sin U cualificadores, con importaciones estticas Java SE5; package net.mindview.til; public class Range ( // Generar una secuencia I0..n! public static intU range(int n) ( int (I result = new int [n] for(int i * 0; i < n; i++) result[i] = 1; return result, i // Generar una secuencia (start..end) public static int[] range(int start, int end> { int sz = end - start; int[] result * new int Isz] j for (int i = 0; i < sz; i++) result[i] * start i;

return resulti

// Generar una secuencia [start..end> con incremento igual a step publc static int U range{int start, int end, int step) ( nt sz (end - start!/step; int[] result = new int fez]; for(int i * 0 i < sz; i++)

resuli[i] = start (i * step); retum result;

} ///:-

A partir de ahora, cuando desarrolle cualquier nueva utilidad que le parezca interesante la podr aadir a su propia biblioteca. A lo largo del libro podra ver que cmo aadiremos ms componentes a la biblioteca nct.mindvicwutil Utilizacin de importaciones para modificar el comportamiento

Una caracterstica que se echa en taita en Java es la compilacin condicional que existe en C y que permite cambiar una variable indicadora y obtener un comportamiento diferente sin variar ninguna otra parte del cdigo. La razn por la que dicha caracterstica no se ha incorporado a Java es, probablemente, porque la mayor parte de las veces se utiliza en C para resolver los problemas mterplataforma: dependiendo de la plataforma de destino se compilan diferentes partes del cdigo. Puesto que Java est pensado para ser automticamente un lenguaje interplataforma no debera ser necesaria.

Sin embargo, existen otras aplicaciones interesantes de la compilacin condicional. Un uso bastante comn es durante la depuracin del cdigo. Las caractersticas de depuracin se activan durante el desarrollo y se desactivan en el momento de lanzar el producto. Podemos conseguirci mismo efecto modificando el paquete que se importe dentro de nuestro programa, con el fin de conmutar entre el codigo utilizado en la versin de depuracin y el empleado en la versin de produccin. Esta misma

tcnica puede utilizarse para cualquier cdigo de tipo condicional.

Ejercicio 3: (2) Cree dos paquetes: debug y debugolT. que contengan una clase idntica con un mtodo debug( ). La

primera versin debe mostrar su argumento String en la consola, mientras que la ?egunda no debe hacer nada. Utilice una lnea static import para importar la clase en un programa de prueba y demuestre el efecto de la compilacin condicional. Un consejo sobre los nombres de paquete

Merece la pena recordar que cada vez que creamos un paquete, estamos especificando implicitamente una estructura de directorio en el momento de dar al paquete un nombre. El paquete debe estar en el directorio indicado por su nombre, que deber ser un directorio alcanzable a partir de la ruta indicada en CLASSPATTL Experimentar con la palabra clave package puede ser algo frustrante al principio, porque a menos que respetemos la regla que establece la correspondencia entre nombres de paquete y rutas de directorio, obtendremos un montn de misteriosos mensajes en tiempo de ejecucin que nos dicen que el sistema no puede encontrar una clase concreta, incluso aunque esa clase est ah en el mismo directorio. Si obtiene un mensaje como ste, desactive mediante un comentario la instruccin package y compruebe si el programa funciona, si lo hace, ya sabe dnde est el problema.

Observe que el cdigo compilado se coloca a menudo en un directorio distinto de aquel en el que reside

el cdigo fuente, pero la ana al cdigo compilado deber seguir siendo localizablc por la JVM utilizando la variable CLASSPATH. Especificadores de acceso Java

Los especificadores de acceso Ja\a puhlic, prntectcd y prvate se colocan delante de cada definicin decada miembro de la clase, ya sea ste un campo o un mtodo. Cada espeeifteador de acceso slo controla el acceso para esa definicin concreta.

Si no proporciona un especificador de acceso, querr decir que ese miembro tiene acceso de paquete. Por tanto, de una forma u otra, todo tiene asociado algn tipo de control de acceso. En las secciones siguientes, vamos a analizar los diversos tipos de acceso. Acceso de paquete

En los ejemplos de los captulos anteriores no hemos utilizado especiPicadores de acceso. El acceso predeterminado no nene asociada ninguna palabra clave, pero comnmente se hace referencia a l como acceso de paquete (y tambin, en ocasiones, "acceso amigable). Este tipo de acceso significa que todas las dems clases del paquete actual tendrn acceso a ese miembro, pero para las clases situadas fuera del paquete ese miembro aparecer como prvate. Puesto que cada unidad de compilacin (cada archivo) slo puede pertenecer a un mismo paquete, todas las clases dentro de una misma unidad de compilacin estarn automticamente disponibles para las otras mediante el acceso de paquete

El acceso de paquete nos permite agrupar en un mismo paquete una serie de clases relacionadas para que puedan interac- tuar fcilmente entre si. Cuando se colocan las clases juntas en un paquete, garantizando as el acceso mutuo a sus miembros definidos con acceso de paquete, estamos en cierto modo garantizando que el cdigo de ese paquete sea propiedad* nuestra. Resulta bastante lgico que slo el cdigo que sea de nuestra propiedad disponga de acceso de paquete al resto del cdigo que nos pertenezca. En cierto modo, podramos decir que el acceso de paquete hace que tenga sentido el agrupar las clases dentro de un paquete. En muchos lenguajes, la forma en que se hagan las definiciones en los archivos puede ser arbitraria. pero en Java nos vemos impelidos a organizaras de una forma lgica. Adems, podemos aprovechar la definicin del paquete para excluir aquellas clases que no deban tener acceso a las clases que se definan en el paquete actual.

Cada clase se encarga de controlar qu cdigo tiene acceso a sus miembros. El cdigo de los restantes paquetes no puede presentarse sin ms y esperar que le muestren los miembros protected. los miembros con acceso de paquete y los miembros prvate de una determinada clase. La nica forma de conceder acceso a un miembro consiste en:

L Hacer dicho miembro public. Entonces, todo el mundo podr acceder a l.

2.

Hacer que ese miembro tenga acceso de paquete, por el procedimiento de no incluir ningn especificador de acceso. y colocar las otras clases que deban acceder a l dentro del mismo paquete. Entonces, las restantes clases del paquete podran acceder a ese miembro.

3.

( orno veremos en el Capitulo 7. Reutitizacin de clases, cuando se introduce la herencia, una

clase heredada puede acceder tanto a los miembros protected como a los miembros public (pero no a los miembros prvate). Esa clase podr acceder a los miembros con acceso de paquete slo si las dos clases se encuentran en el mismo paquete. Pero, por el momento, vamos a olvidamos de los temas de herencia y del espee i fie ador de acceso protected.

4.

Proporcionar mtodos de acceso minadores (tambin denominados mtodos get set") que permitan leer y cambiar el valor ste es el enfoque ms civilizado en trminos de programacin orientada a objetos, y resulta fundamental en JavaBeans. como podr ver en el Capitulo 22. Interfaces grficas de usuario.

public: acceso de interfaz

Cuando se utiliza la palabra clave public. esta quiere decir que la declaracin de miembros situada inmediatamente a continuacin suya esui disponible para todo el mundo, y en particular para el programa cliente que utilice la biblioteca. Suponga que definimos un paquete dessert que contiene la siguiente unidad de compilacin. //< access/dessert/Cookie. lava i ) Crea una biblioteca, package accesa.dessert; public clase Cookie ( public Cookie() j System.out.printlnI"Cookie constructor"!j

) void bite<) ( System.out.printlnCbite") }

} ///:-

Recuerde que el archivo de clase producido por C'ookie.java debe residir en un subdirectorio denominado dessert, dentro de un directorio access (que hace referencia al Capitulo 6, Contml de acceso de este libro) que a su vez deber estar bajo uno de los directorios CLASSPATH. No cometa el error de pensar que Java siempre examinara el directorio actual como uno de los puntos de partida de su bsqueda. Si no ha incluido un como una de las rutas dentro de CLASSPATH. Java no examinar ese directorio.

Si ahora creamos un programa que usa Cookie: //: access/Dinner.ja va // Usa la biblioteca, import access.dessert. ; public class Dinner { public static void mam {String [] argsj ( Cookie x = new Cookie () ; //i x.bite); // No puede acceder

) } } * Output t Cookie constructor *///:-

podemos crear un objeto Cookie. dado que su constructor es public y que la clase tambin es public (ms adelante, profundizaremos ms en el concepto de clase public). Sin embargo, el miembro bite( ) es inaccesible desde de Dinncr.java ya que bite( ) slo proporciona acceso dentro del paquete dessert.

asi que el compilador nos impedir utilizarlo. El paquete predeterminado

Puede que le sorprenda descubrir que el siguiente cdigo si que puede compilarse, a pesar de que parece que no cumple con las reglas: //: access/Cake.java // Accede a una clase en una unidad de compilacin separada. class Cake { public static void main (String [] args) ( Pie x * new Pie(J; x.f H ;

) } / * Output: Pie.fi)

*///:-

En un segundo archivo del mismo directorio tenemos: //: access/Pie.jav a J t La otra clase. class Pie ( void () ( System.out-println(Pie.fO">j }

) //>*-

Inicialmente, cabra pensar que estos dos archivos no tienen nada que ver entre si. a pesar de lo cual Cake es capaz de crear un objeto Pie y de invocar su mtodo f( ) (observe que debe tener en su variable CLASSPATH para que los archivos se compilen). Lo que parecera lgico es que Pie y f ) tengan acceso de paquete y no estn, por tanto, disponibles para Cake Es verdad que tienen acceso de paquete, esa pane de la suposicin es correcta. Pero la razn por la que estn disponibles en Cake.java es porque se encuentran en el mismo directorio y no tienen ningn nombre explcito de paquete. Java trata los archivos de este upo como si fueran implcitamente parte del paquete predeterminado HM de ese directorio, y por tanto proporciona acceso de paquete a todos los restantes archivos situados en ese directorio. prvate: no lo toque!

La palabra clave prvate significa que nadie puede acceder a ese miembro salvo la propia clase que lo contiene, utilizando para el acceso los propios mtodos de la clase. F.I resto de las clases del mismo paquete no puede acceder a los miembros privados, as que el efecto resultante es como si estuviramos protegiendo a la clase contra nosotros mismos. Por otro lado resulta bastante comn que un paquete sea creado por varias personas que colaboran entre si, por lo que prvate permite modificar libremente ese miembro sin preocuparse de si afectar a otras clases del mismo paquete.

El acceso de paquete predeterminado proporciona a menudo un nivel adecuado de ocultacin; recuerde que un miembro con acceso de paquete resulta inaccesible para todos los programas cliente que utilicen esa clase. Esto resulta bastante conveniente. ya que el acceso predeterminado es el que normalmente se utiliza (y el que se obtiene si nos olvidamos de aadir especificadores de control de acceso). Por tanto, lo que normalmente haremos ser pensar qu miembros queremos definir explcitamente como pblicos para que los utilicen los programas cliente: como resultado, uno tendera a pensar que la palabra clave prvate no se utiliza muy a menudo, ya que se pueden realizar los diseos sin ella. Sin embargo, la realidad es que el uso coherente de prvate tiene una gran importancia, especialmente en el caso de la programacin multihebra (como veremos en el Captulo 21, Concurrencia).

lie aqu un ejemplo del uso de prvate: . access/IceCream.java //

Ilustra la palabra clave prvate". class Sundae { private Sundae() {} static Sundae makeASundae{) ( return new Sundae( ) ; ) l public class IceCream ( public static void main(String[] args) ( //i Sundae x *= new Sundae (l ; Sundae x = Sundae .makeASundae 0 ;

} ///:-

Este ejemplo nos permite ver un caso en el que prvate resulta muy til: queremos tener control sobre el modo en que se crea un objeto y evitar que nadie pueda acceder directamente a un constructor concreto (o a todos ellos). En el ejemplo anterior, no podemos crear un objeto Sundae a travs de su constructor, en lugar de ello, tenemos que invocar el mtodo makeASundae( ) para que se encargue de hacerlo por nosotros.16

Existe otro efecto en c*te caso: puesto que el nico coastruclor definido es el predeterminado y este se ha definido como prvate, se impedir que nadie herede esta clase {lo cual es un lema del que hablaremos ms adelante)
16

Cualquier mtodo del que estemos seguros de que slo acta como mtodo auxiliar de la clase puede ser definido como privado para garantizar que no lo utilicemos accidentalmente en ningn otro lugar del paquete, lo que nos impedira modificar o eliminar el mtodo. Definir un mtodo como privado nos garantiza que podamos modificarlo libremente en el futuro.

Lo mismo sucede para los campos privados definidos dentro de una clase. A menos que tengamos que exponer la imple- mentacin subyacente (lo cual es bastante menos habitual de lo que podra pensarse), conviene definir todos los campos como privados. Sin embargo, el hecho de que una referencia a un objeto sea de tipo prvate en una clase no quiere decir que algn otro objeto no pueda tener una referencia de tipo public al mismo objeto (consulte los suplementos en lnea del libro para conocer ms detalles acerca de los problemas de los alias). protected: acceso de herencia

Para poder comprender el especifcador de acceso protected, es necesario que demos un salto hacia adelante. En primer lugar, debemos tener presente que no es necesario comprender esta seccin para poder continuar leyendo el libro hasta llegar al captulo dedicado a la herencia (el Captulo 7. Reutilizacin ele clases). Pero para ser exhaustivos, hemos incluido aqu una breve descripcin y un ejemplo de uso de protected.

La palabra clave protected trata con un concepto denominado herencia, que toma una clase existente (a la que denominaremos clase base) y aade nuevos miembros a esa clase sin tocar la clase existente. Tambin se puede modificar el comportamiento de los miembros existentes en la clase. Para heredar de una clase, tenemos que especificar que nuestra nueva clase amplia (extends) una clase existente, como en la siguiente lnea: class Foo extends Bar {

El resto de la definicin de la clase tiene el aspecto habitual.

Si creamos un nuevo paquete y heredamos de una clase situada en otro paquete, los nicos miembros a los que tendremos acceso son los miembros pblicos del paquete original (por supuesto, si la herencia se realiza dentro del mismo paquete, se podrn manipular todos los miembros que tengan acceso de paquete). En ocasiones, el creador de la clase base puede tomar un miembro concreto y garantizar el acceso a las clases derivadas, pero no al mundo en general. Eso es precisamente lo que hace la palabra clave protected Esta palabra clave tambin proporciona acceso de paquete, es decir, las resiantes clases del mismo paquete podrn acceder a los elementos protegidos.

Si volvemos al archivo Cookie.java. la siguiente clase no puede invocar el miembro bite( ) que tiene acceso de paquete: //: access/ChocolateChip.java // No se puede usar un miembro con acceso de paquete desde otro paquete, impon access. dessert. * ; public class ChocolateChip extends Cookie | public ChocolateChip(I ( System.out .println (* ChocolateChip constructor**);

) public vod chompO ( //! biteO; // No se puede acceder a bite

) public static void mainlString[] argsj ( ChocolateChip x * new ChocolateChipO; x.chomp(J;

) ) / Output: Cookie constructor ChocolateChip constructor *///:-

Uno de los aspectos interesantes de la herencia es que. si existe un mtodo bite( ) en la clase Cookie. tambin existir en todas las clases que hereden de Cookie Pero como blte( ) tiene acceso de paquete y est situado en un paquete distinto, no estar disponible para nosotros en el nuevo paquete. Por supuesto, podramos hacer que ese mtodo fuera pblico, pero entonces todo el mundo tendra acceso y puede que no sea eso lo que queramos. Si modificamos la clase Cookie de la forma siguiente: //: access/cookie2/Cookie.java package access.cookie2; public class Cookie { public Cookieo { SysLem.out.printlnl"Cookie constructor");

) protected void biteO ( Svstem.out.printlnI"bite" ) ;

) i m-

ahora bite( ) estara disponible para toda aquella clase que herede de Cookie: : access/Chocc-lateChip2. java import access.ccokie2. public class ChocolateChip2 extends Cookie ( public ChocolateChipZ() ( System.out.Drintln("ChocclateChip2 constructor);

) public void chompO ( bite() / } // Mtodo protegido public static vod mainfStringH args) { ChocolateChip2 x = new ChocolateChip20; x.chomp();

} ] /* Output: Cookie constructor ChocolateChip2 constructor bite *///:-

Observe que. aunque bitc( ) tambin tiene acceso de paquete, no es de tipo public

Ejercicio 4: (2) Demuestre que los mtodos protegidos (protcctcd) tienen acceso de paquete pero no son pblicos

Ejercicio 5:(2) Cree una clase con campos y mtodos de tipo public. prvate, prntected y con acceso de paquete. Cree

un objeto de esa clase y vea los tipos de mensajes de compilacin que se obtienen cuando se intenta acceder a lodos los miembros de la clase Tenga en cuenta que las clases que se encuentran en el mismo directorio forman parte del paquete predeterminado.

Ejercicio 6: (!) Cree una clase con datos protegidos. Cree una segunda clase en el mismo archivo con un mtodo que

manipule los datos protegidos de la primera clase. Interfaz e implementacin

El mecanismo de control de acceso se denomina a menudo ocultacin <ic la implementacin. El envolver los datos y los mtodos dentro de la clase, en combinacin con el mecanismo de ocultacin de la implementacin se denomina a menudo encapsulacin.M3 El resultado es un tipo de datos con una serie de caractersticas y comportamientos.

El mecanismo de control de acceso levanta una serie de fronteras dentro de un tipo de datos por dos razones importantes. La primera es establecer qu es lo que los programas cliente pueden usar o no. Podemos entonces disear como queramos los mecanismos internos dentro de la clase, sin preocupamos de que los programas cliente utilicen accidentalmente esos mecanismos internos com) parte de la interfaz que deberan estar empleando.

Esto nos lleva directamente a la segunda de las razones, que consiste en separar la interfaz de la implementacin. Si esa clase se utiliza dentro de un conjunto de programas, pero los programas cliente tan slo pueden enviar mensajes a la interfaz pblica, tendremos libertad para modificar cualquier cosa que no sea pblica (es decir, los miembros con acceso de paquete, protegidos o privados) sin miedo de que el cdigo cliente deje de funcionar.

Para que las cosas sean ms claras, resulta conveniente a la hora de crear las clases, situar los miembros pblicos al principio. seguidos de los miembros protegidos, los miembros con acceso de paquete y los miembros privados. La ventaja es que el usuario de la clase puede comenzar a leer desde el principio y ver en primer lugar lo que para el es lo ms importante (los miembros de tipo public. que son aquellos a los que podr accederse desde fuera del archivo), y dejar de leer en cuanto encuentre los miembros no pblicos, que forman parte de la implementacin iniema. //: access/Organi ava public

zedByAccess.j

cla9s QrganizedByAccess ( private int i; // ...

} ///:Sin embargo, mucha genie utili/ la palabra cncapNU tac iti para retemse en exclusiva a la insultacin de la implementacin.
5

Esto slo facilita parcialmente la lectura, porque la interfaz y la implementacin siguen estando mezcladas En otras palabras. el lector seguir pudiendo ver el cdigo fuente (la implementacin). porque est incluido en la clase. Adems, el sistema de documentacin basado en comentarios soportado por Javadoc no concede demasiada importancia a la legibilidad del cdigo por parte de los programadores de clientes. El mostrar la interfaz al consumidor de una clase es, cu realidad, trabajo del explorador de clases, que es una herramienta que se encarga de examinar lodas las clases disponibles y mostramos

lo que se puede hacer con ellas (es decir, qu miembros estn disponibles) en una forma apropiuda. En Java, visualizar la documentacin del JDK con un explorador web nos proporciona el mismo resultado

que si utilizramos un explorador de clases. Acceso de clase

En Java, los especificadores de acceso tambin pueden emplearse para determinar qu clases de una biblioteca estarn disponibles para los usuarios de esa biblioteca. Si queremos que una cierta clase est disponible para un programa cliente, tendremos que utilizar la palabra clave public en la definicin de la propia clase. Con esto se controla si los programas cliente pueden siquiera crear un objeto de esa clase.

Para controlar el acceso a una clase, el especificador debe situarse delante de la palabra clave class. Podemos escribir public class Widget (

Ahora, si el nombre de la biblioteca es access. cualquier programa cliente podr acceder a Widget mediante la instruccin import access.Widget/

import access.;

Sin embargo, existen una serie de restricciones adicionales:

1.

Slo puede haber una clase public por cada unidad de compilacin (archivo). La idea es que cada unidad de compilacin tiene una nica interfaz pblica representada por esa clase pblica. Adems de esa clase, puede lener tantas clases de soporte con acceso de paquete como deseemos. Si tenemos ms de una clase pblica dentro de una unidad de compilacin, el compilador generar un mensaje de error.

2.

El nombre de la clase public debe corresponderse exactamente con el nombre del archivo que contiene dicha unidad de compilacin, incluyendo el uso de maysculas y minsculas. De modo que para Widget. el nombre del archivo deber ser Widget.java y no uidget.java ni WIDGET.java. De nuevo, obtendremos un error en tiempo de compilacin si los nombres no concuerdan.

3.

Resulta posible, aunque no muy normal, tener una unidad de compilacin sin ninguna clase public. Ln este caso, podemos dar al archivo el nombre que queramos (aunque si lo denominamos de forma arbitraria confundiremos a las personas que tengan que leer y mantener el cdigo)

Qu sucede si tenemos una clase dentro de access que slo estamos empleando para llevar a cabo las tareas realizadas por Widget o alguna otra clase de tipo public de access? Puede que no queramos molestamos en crear la documentacin para el programador de clientes y que pensemos que algo ms adelante quiz queramos modificar las cosas completamente, eliminando esa clase y sustituyndola por otra. Para poder disponer de esta flexibilidad, necesitamos garantizar que ningn programa cliente dependa de nuestros detalles concretos de implementacin que estn ocultos dentro de access Para conseguir esto, basta con no incluir la palabra clave public en la clase, en cuyo caso tendr acceso de paquete (dicha clase slo podr ser usada dentro de ese paquete).

Ejercicio 7: (I) Cree una biblioteca usando los fragmentos de cdigo con los que hemos descrito access y Widget.

Cree un objeto Widget dentro de una clase que no forme parte del paquete access.

Cuando creamos una clase con acceso de paquete, sigue siendo conveniente definir los campos de esa clase como prvate (siempre deben hacerse los campos lo ms privados posible), pero generalmente resulta razonable dar a los mtodos el mismo tipo de acceso que a la clase (acceso de paquete). Puesto que una clase con acceso de paquete slo se utiliza normalmente dentro del paquete, slo har falta definir como pblicos los mtodos de esa clase si nos vemos obligados a ello; adems. en esos casos, el compilador ya se encargara de informamos.

Observe que una clase no puede ser prvate (ya que eso hara que fuera inaccesible para todo el inundo

salvo para la propia clase) ni protected.6 Asi que slo tenemos dos opciones para especificar el acceso a una clase: acceso de paquete o public Si no queremos que nadie tenga acceso a la clase, podemos definir todos los constructores como privados, impidiendo que nadie cree un objeto de dicha clase salvo nosotros, que podremos hacerlo dentro de un miembro de tipo static de esa clase. He aqu un ejemplo: / / : access/Lunch.java // Ilustra los especiicadores de acceso a clases. Define una // clase como privada con constructores privados: class Soupl { prvate SouplO {) // (1) Permite la creacin a travs de un mtodo esttico: public static Soupl makeSoupO ( retum new Soupl () ,17

} class 5oup2 f prvate Soup2() (} // (2) Crea un objeto esttico y devuelve una referencia // cuando se solicita.(El patrn "Singleton"): private static Soup2 psl = new Soup2O; public static Soup2 access) ( retum psl;

En realidad, una dase interna puede ser privada o protegida, pero se traa de un caso opectal Hablaremos de ello en el Capitulo 10, Clases internas
17

) public void f C) (} i // Slo se permite una clase pblica por archivo: public class Lunch ( void testPrivate(> { // |No se puede hacer! Constructor privado: ) / / i Soupl soup = new Soupl O

void testStaticO ( SOUDI soup = Soupl.makeSouD();

> void testSingleton0 { Soup2. access ()*{)'?

) ///:-

Hasia ahora, la mayora de los mtodos devolvan void o un tipo primitivo, por lo que la definicin: public static Soupl makeSoupO { retum new Soupl () ; i

puede parecer algo confusa a primera vista. La palabra Soupl antes del nombre del mtodo (makeSoup) dice lo que el mtodo devuelve. Hasta ahora en el libro, esta palabra normalmente era void. lo que significa que no devuelve nada. Pero tambin podemos devolver una referencia a un objeto, que es lo que estamos haciendo aqui. Este mtodo devuelve una referencia a un objeto de la clase Soupl.

Las clases Soupl y Soup2 muestran cmo impedir la creacin directa de objetos de una clase definiendo lodos los constructores como privados. Recuerde que, si no creamos explcitamente al menos un constructor, se crear automticamente el constructor predeterminado (un constructor sin argumentos). Escribiendo el constructor predeterminado, garantizamos que no sea escrito automticamente. Definindolo como privado nadie podr crear un objeto de esa clase. Pero entonces, cmo podr alguien usar esta clase? En el ejemplo anterior se muestran dos opciones l n Soup 1. se crea un mtodo static que crea un nuevo objeto Soupl y devuelve una referencia al mismo, listo puede ser til si queremos realizar algunas operaciones adicionales con el objeto Soupl antes de devolverlo, o si queremos llevar la cuenta de cuntos objetos Soupl se han creado (por ejemplo, para restringir el nmero total de objetos).

Soup2 utiliza lo que se denomina un patrn de diseo, de lo que se habla en Thinking in Pattems fwith Java) en wwH.MindHeu net. Este patrn concreto se denomina Solitario (singieton), porque slo permite crear un nico objeto. El objeto de la clase Soup2 se crea como un miembro static prvate de Soup2, por lo que existe un objeto y slo uno. y no so puede acceder a l salvo a travs del mtodo pblico acccss( ).

Como hemos mencionado anteriormente, si no utilizamos un especificador de acceso para una clase, sta tendr acceso de paquete. Esto significa que cualquier otra clase del paquete podr crear un objeto de esa clase, pero no se podrn crear objetos de esa clase desde fuera del paquete (recuerde que todos los archivos de un mismo directorio que no tengan declaraciones package explcitas forman parte, implcitamente, del paquete predeterminado de dicho directorio). Sin embargo, si un miembro esttico de esa clase es de tipo public. el programa cliente podr seguir accediendo a ese miembro esttico, an cuando no podr crear un objeto de esa clase.

Ejercicio 8: (4) Siguiendo la forma del ejemplo Lunch.java. cree una clase denominada ConnectionManager que

gestione una matriz fija de objetos Connection. El programa cliente no debe poder crear explcitamente objetos Connection. sino que slo debe poder obtenerlos a travs de un mtodo esttico de ConnectionManacer. Cuando ConnectionManager se quede sin objetos, devolver una referencia nuil Pruebe las clases con un programa main( ).

Ejercicio 9: (2)Creeel siguiente archivo en el directorio access/local (dentro de su ruta CLASSPATH): // access/local/PackagedClas s.java package access.local; ciass PackagedClass ( public PackagedClass0 { System.out.printlnt"Creating a oackaged classJ;

A continuacin cree el siguiente archivo en un directorio distinto de access/local: // access/foreign/Foreign.ja va packaqe access.t oreign; import access.local.*; public class Foieign { public static veid mam<String 11 args) { PackagedClass pe = new PackagedClass (.);

Explique por qu el compilador crea un error. Se resolvera el error si la clase Forci^n fuera parte del paquete accessJocal? Resumen

En cualquier relacin, resulta importante definir una serie de fronteras que sean respetadas por todos los participantes. Cuando se crea una biblioteca, se establece una relacin con el usuario de esa biblioteca (el programador de clientes), que es un programador como nosotros, aunque lo que hace es utilizar la biblioteca para construir una aplicacin u otra biblioteca de mayor tamao.

Sin una serie de reglas, los programas cliente podran hacer lo que quisieran con todos los miembros de una clase, an cuando nosotros prefiriramos que no manipularan algunos de los miembros. Todo esuiria expuesto a ojos de todo el mundo.

En este capitulo hemos visto cmo se construyen bibliotecas de clases en primer lugar. la forma en que se puede empaquetar un grupo de clases en una biblioteca y. en segundo lugar, la forma en que las clases pueden controlar el acceso a sus

miembros.

Las estimaciones indican que los proyectos de programacin en C empiezan a fallar en algn punto entre las 50,000 y 100.000 lineas de cdigo, porque C tiene un nico espacio de nombres y esos nombres empiezan a colisionar, obligando a realizar un esfuerzo adicional de gestin. En Java, la palabra elave paekage. el esquema de denominacin de paquetes y la palabra clave import nos proporcionan un control completo sobre los nombres, por lo que puede evitarse fcilmente el problema de la colisin de nombres.

Existen dos razones para controlar el acceso a los miembros La primera consiste en evitar que los usuarios puedan husmear en aquellas partes del cdigo que no deben tocar. Esas partes son necesarias para la operacin interna de la clase, pero no forman parte de la interfaz que el programa cliente necesita Por tanto, definir los mtodos y los campos comn privados constituye un servicio para los programadores de clientes, porque asi stos pueden ver fcilmente qu cosas son .mportan- tes para ellos y qu cosas pueden ignorar. Simplifica su comprensin de la clase.

La segunda razn, todava ms importante, para definir mecanismos de control de acceso consiste en permitir al diseador de bibliotecas modificar el funcionamiento interno de una clase sin preocuparse de si ello puede afectar a los programas cliente Por ejemplo, podemos disear al principio una clase de una cierta manera y luego descubrir que una cierta reestructuracin del cdigo podra aumentar enormemente la velocidad. Si la interfaz y la implementacin estn claramente protegidas. podemos realizar las modificaciones sin forzar a los programadores de clientes a reescribir su cdigo. Los mecanismos de control de acceso garantizan que ningn programa cliente dependa de la implementacin subyacente de una clase.

Cuando disponemos de la capacidad de modificar la implementacin subyacente, no slo tenemos la libenad de mejorar nuestro diseo, sino tambin la libertad de cometer errores. Independientemente del cuidado que pongamos en la planificacin y el diseo, los errores son inevitables. Saber que resulta relativamente seguro cometer esos errores significa que tendremos menos miedo a experimentar, que aprenderemos ms rpidamente y que finalizaremos nuestro proyecto con mucha ms antelacin.

La interfaz pblica de una clase es lo que el usuario puede ver. as que constituye la parte de la clase que ms importancia tiene que definamos correctamente" durante la tase de anlisis del diseo. Pero incluso aqu existen posibilidades de modificacin. Si no definimos correctamente la interfaz a la primera podemos aadir ms mtodos siempre y cuando no eliminemos ningn mtodo que los programas cliente ya hayan utilizado en su cdigo

Observe que el mecanismo de control de acceso se centra en una relacin (y en un tipo de comunicacin) entre un creador de bibliotecas y los clientes externos de esas bibliotecas. Existen muchas situaciones en las que esta relacin no est presente Por ejemplo, imagine que todo el cdigo que escribimos es para nosotros mismos o que estamos trabajando en estrecha relacin con un pequeo grupo de personas y que todo lo que se escriba se va a incluir en un mismo paquete. En esas situaciones, el tipo de comunicacin es diferente, y la rgida adhesin a una serie de reglas de acceso puede que no sea la solucin ptima. En esos casos, el acceso predeterminado (de paquete) puede que sea perfectamente adecuado.

t.d^ mUiciono u lo ejercicio seleccionados pueden encontrarse en el documento electrnico 77#r Thtt\kin% n Java AWWLIUJ Soiulnm ChuU , dispouiblc para la venia en inm MindDi-w iwReutilizacin de clases

7 Una de las caractersticas ms atractivas de Java es la posibilidad de reutilizar el cdigo. Pero, para ser verdaderamente revolucionario es necesario ser capaz de hacer mucho ms que simplemente copiar el cdigo y modificarlo.

sta es la tcnica que se ha estado utilizando en lenguajes procedimemales como C. y no ha funcionado muy bien. Como todo lo dems en Java, la solucin que este lenguaje proporciona gira alrededor del concepto de clase. Reuttlizando el cdigo creamos nuevas clases, pero en lugar de crearlo partiendo de cero, usamos las clases existentes que alguien haya construido y depurado anteriormente.

t i truco estriba en utilizar las clases sin modificar el cdigo existente. En este capitulo vamos ver dos formas de llevar esto a cabo. La primera de ellas es bastante simple. Nos limitamos a crear objetos de una clase ya existente dentro de una nueva clase Esto se denomina composicin, porque la nueva clase est compuesta de objetos de otras clases existentes. Con esto, simplemente estamos reutilizando la funcionalidad del cdigo, no su forma.

La segunda tcnica es ms sutil. Se basa en crear una nueva clase como un tipo Je una clase existente. Literalmente lo que hacemos es tomar la forma de la clase existente y aadirla cdigo sin modificarla. Esta tcnica se denomina herencia. y el compilador se encarga de realizar la mayor parte del trabajo. La

herencia es una de las piedras angulares de la programacin orientada a objetos y tiene una serie de implicaciones adicionales que analizaremos en el Capitulo 8. Poliformismo.

Resulta que buena parte de la sintaxis y el comportamiento son similares tanto en la composicin como en la herencia (lo que tiene bastante sentido, ya que ambas son formas de construir nuevos tipos a partir de otros tipos existentes). En este capitulo, vamos a presentar ambos mecanismos de reutilizacin de cdigo. Sintaxis de la composicin

Hemos utilizado el mecanismo de composicin de forma bastante frecuente en el libro hasta este momento. Simplemente, basta con colocar referencias a objetos dentro de las clases. Por ejemplo, suponga que queremos construir un objeto que almacene varios objetos Sfrlng, un par de primitivas y otro objeto de otra clase. Para los objetos no primitivos, lo que hacemos es colocar referencias dentro de la nueva clase, pero sin embargo las primitivas se definen directamente: //: reusing/SprinklerSystem.java // Composicin para la reutilizacin de cdigo. class WaterSource { private String s; WaterSource(I { System.out.println("WaterSo urce() ") ; s "Constructed" ,

i public String toStrmgt) ( return s; )

) public class SprinklerSystem { private String valvel, valve2, valve3, valve4;

private WaterSource source = new WaterSourcei); prvate nt i; private float f; public String toString) { return "valvel* " valvel N M + " " M\n" + "valve=MM + valve2+"valve3= M + valve34 "valve4= M + valve4 "source= " aource;

} public static void main(StringU args) { SorinklerSystem sprinklers = new SprinklerSystem); System.out.println(sprinklers*

) } / Output: WaterSource() valvel = nuil valve2 = nuil valve3 = nuil valve4 = nuil i - 0 f * 0.0 source *= Constructea ///:-

Uno de los mtodos definidos en ambas clases es especiaJ: toString). Todo objeto no primitivo tiene un mtodo toString) que se invoca en aquellas situaciones especiales en las que el compilador quiere una cadena de caracteres String pero lo que tiene es un objeto. Asi. en la expresin contenida en SprinklerSystem.toString ): "source " + source?

el compilador ve que estamos intentando aadir un objeto String ("source = ") a un objeto WaterSource. Puesto que slo podemos aadir'* un objeto String a otro objeto String, el compilador dice voy a convertir source en un objeto String invocando toString )!** Despus de hacer esto, puede combinar las dos cadenas de caracteres y pasar el objeto String resultante a S>stcm.out.println() (o. de forma equivalente, a los mtodos estticos print( ) y printnb() que hemos definido en este libro) Siempre que queramos permitir este tipo de comportamiento dentro de una clase que creemos, nos bastar con escribir un mtodo toString).

Las primitivos que son campos de una clase se inicializan automticamente con el valor cero, como hemos indicado en el Capitulo 2. Todo es un objeto Pero las referencias a objetos se inicializan con el valor nuil, y si tratamos de invocar mtodos para cualquiera de ellas obiendremos una excepcin (un error en tiempo de ejecucin). Afortunadamente, podemos imprimir una referencia nuil sin que se genere una excepcin.

Tiene bastante sentido que el compilador no cree un objeto predeterminado para todas las referencias, porque eso obligara a un gasto adicional de recursos completamente innecesarios en muchos casos. Si queremos micializar las referencias, piulemos hacerlo de las siguientes formas:

1.

En el lugar en el que los objetos se definan. Esto significa que estarn siempre inicial izados antes de que se invoque el constructor

2.

En el constructor correspondiente a esa clase.

3.

4.

Justo antes de donde se necesite utilizar el ohjeto. Esto se suele denominar iniciulizacan diferida. Puede reducir el gasto adicional de procesamiento en aquellas situaciones en las que la creacin de los objetos resulte muy cara y no sea necesario crear el objeto todas las veces. Utilizando la tcnica de inicializacin de instancia.

El siguiente ejemplo ilustra las cuatro tcnicas: //: reusmg/Bath.java // Inicializacin mediante constructor con composicin, import static net.mindview.util.Print.*; class Soap ( private String s; Soap() { print I"Soap()"); s * "Constructed** ;

) public String toStringO { return s; }

) public class Eath ( private String ff Inicializacin en el punto de definicin: si = "Happy"4 s2 "Happy", s3, s4; private Soap castille; prvate int i; private float toy; public Bath() { print ("Inside Bathd")/ s3 * "Joy"; toy = 3.14f; castille * new SoapO;

) // Inicializacin de instancia: ( 1 - 47;) public String toStrlngI) { if(s4 * null) ft Inicializacin diferida: s4 = "Joy"; return "i * " 4 i 4 "\n" 4 "toy = " 4 - toy + "\n" "castille = " -* castille;

public static void main{String[] args) ( Bath b = new Bath I); print(b); ) ) /* Output: Inside BathO Soap I) si * Happy s2 = Happy s3 Joy s4 Joy i a 47 toy * 3.14 castille = Constructed *///:-

Observc que en el constructor Bath, se ejecuta una instruccin antes de que tenga lugar ninguna de las inicializaciones. Cuando no se realiza la inicializacin en el punto de definicin, sigue sin haber ninguna garanta de que se vaya a realizar la inicializacin antes de enviar un mensaje a una referencia al objeto, salvo la inevitable excepcin en tiempo de ejecucin.

Cuando se invoca toString( ). asigna un valor a s4 de modo que todos los campos estarn apropiadamente inicial izados en el momento de usarlos.

Ejercicio 1:(2) Cree una clase simple. Dentro de una segunda clase, defina una referencia a un objeto de la segunda

clase. Utilice el mecanismo de inicializacin diferida para instanciar este objeto. Sintaxis de la herencia

La herencia es una parte esencial de Java (y todos los lenguajes orientados a objetos). En la prctica, siempre estamos utilizando la herencia cada vez que creamos una clase, porque a menos que heredemos explcitamente de alguna otra clase, estaremos heredando implcitamente de la clase raz estndar Object de Java.

La sintaxis de composicin es obvia, pero la herencia utiliza una sintaxis especial. Cuando heredamos, lo que hacemos es decir esta nueva clase es similar a esa antigua clase. Especificamos esto en el cdigo situadoantes de la llave de abertura del cuerpo de la clase, utilizando la palabra clave extends seguida del nombre de laclase base. Cuandohacemos esto*

automticamente obtenemos todos los campos y mtodos de la clase base He aqui una clase. //: reusmg/Detergent. java // Sintaxis y propiedades de la herencia. import static net .mindvi.ew.util. Print. * ? class Cleanser ( private String s = "Cleanser" public void appendString a) ( s *= a; ) public void dlluteIJ { appendl" dilateIJ"); ) public void applyi) ( append'" apply() "I ,* | public void scrubO { append f" scrub O "M ) public String

toStringO { retum s; | public static void main(StringU argel ( Cleanser x = new Cleansert;,* x.diiutel; x. apply (J; x. scrubO; print UJ ;

) public class Detergent extends Cleanser ( / / Cambio de un mtodo: public void scrubO { append(" Detergent.scrub 0"); 6Uper.scrub0; // Invocar versin de la clase base

) /,/ Aadir mtodos a la interfaz: public void team O ( append" foaml"); } // Probar la nueva clase: public static void main(StringU args) { Detergent x = new Detergent( ) x. di luteO i x applyl); x.scrub(;

x.foaml); printIxl; print("Testing base class:"); i Cleanser.main(argsl;

) / Output: Cleanser diluteu apply 0 Detergent .scrub ( i scrub i) foamO Testing base class: Cleanser dilutel) aoplyO scrub0

Esto nos permite ilustrar una serie de caractersticas. En primer lugar, en el mtodo Cleanser append(). se concatenan cadenas de caracteres con s utilizando el operador +=. que es uno de los operadores, junto con *+*, que los diseadores de Java han sobrecargado para que funcionen con cadenas de caracteres.

7 Reutilizacin de clases 401 En segundo lugar, tanto Cleanser como Detergen! contienen un mtodo main( ). Podemos crear un mtodo ntainf ) para cada una de nuestras clases; esta tcnica de colocar un mtodo maini) en cada clase permite probar fcilmente cada una de ellas. Y no es necesario eliminar el mtodo man( ) cuando hayamos terminado; podemos dejarlo para las pruebas posteriores.

An cuando tengamos muchas clases en un programa, slo se invocar el mtodo main( ) de la clase especificada en la linea de comandos. Por tanto, en este caso, cuando escribimos java Detergen!, se invocar Detergenf.main( ). Pero tambin podemos escribir jasa Cleanser para invocar a Cleanser.main( ). an cuando Gleanser no sea una clase pblica. Incluso aunque una clase tenga acceso de paquete, si el mtodo main( ) es pblico, tambin se podr acceder a l

Aqui. podemos ver que Detergent.main( ) llama a Cleanser.main) explcitamente, pasndole los mismos argumentos de la linea de comandos (sin embargo, podramos pasarle cualquier matriz de objetos String)

Es importante que todos los mtodos de Cleanser sean pblicos. Recuerde que. si no se indica ningn cspeeificador de acceso. los miembros adoptaran de forma predeterminada el acceso de paquete, lo que slo permite el acceso a los otros miembros del paquete Por tamo, dentro de este paquete, cualquiera podra usar esos mtodos si no hubiera ningn especifieador de acceso Detergent. por ejemplo, no tendra ningn problema Sin embargo, si alguna clase de algn otro paquete fuera a heredar de Cleanser. slo podri.i acceder a los miembros de tipo public Asi que. para permitir la herencia, como regla general deberemos definir todos los campos como prvate y lodos los mtodos como public (los miembros de tipo protected tambin permiten el acceso por parte de las clases derivadas; analizaremos este tema ms adelante). Por supuesto, en algunos casos particulares ser necesario hacer excepciones, pero estu directriz suele resultar bastante til.

7 Reutilizacin de clases 402 Cleanser dispone de una serie de mtodos en su interfaz: appendi ). dlute( ). applv( ). seruli( > \ ! oStrng( ), Puesto que Detergent deriva de Cleanser (gracias a la palabra clave e\tend\K automticamente obtendr todos estos mtodos como parte de su intertaz. an cuando no los veamos explcitamente definidos en Detergen! Por tanto, podemos considerar la herencia como un modo de reutilizar la clase.

Como podemos ver en scrul>( ). es posible tomar un mtodo que haya sido definido en la clase base y modificarlo En este caso, puede tambin que queramos invocar el mtodo de la clase base desde dentro de la nueva versin Pero dentro de scrubf ). no podemos simplemente invocar a serub ). ya que eso producida una llamada recursiva, que no es exactamente lo que queremos Pura resolver este problema, la palabra clave super de Java hace referencia a la superclse de la que ha heredado la clase actual Por tanto, la expresin super.scmb( ) invoca a la versin de la cluse base del mtodo serub )

Cuando se hereda, no estamos limitados a utilizar los mtodos de la clase base. Tambin podemos aadir nuevos mtodos a la clase derivada, de la misma forma que los aadiramos a otra clase: definindolos. El mtodo foamt ) es un ejemplo de esto.

En Detergen!.main( ) podemos ver que, pata un objeto Detergen!, podemos invocar todos los mtodos disponibles en Cleanser asi como eri Detergen! (es decir. foam( ))

Ejemplo 2: (2) Cree una nueva clase que herede de la clase Detergen! Sustituya el mtodo serubt )

7 Reutilizacin de clases 403 aada un nuevo

mtodo denominado stcTlize{ ). Inicializacin de la clase base

Puesto que ahora tenemos dos clases (la clase base y la clase derivada) en lugar de una. puede resultar un pin;o confuso tratar de imaginar cul es el objeto resultante generado por una clase derivada. Desde lucra, parece como si la nueva clase tuviera la misma interfaz que la clase base y. quiz algunos mtodos y campos adicionales. Pero el mecanismo de herencia no se limita a copiar la interfaz de la clase base. Cuando se crea un objeto de la clase derivada, ste contiene en su interior un xuhobjeto de la clase base. Este subobjeto es idntico a lo que tendramos si hubiramos creado directamente un objeto de la clase base. Lo que sucede, simplemente, es que, visto desde el exterior, el subobjeto de la clase base est envuelto por el objeto de la clase derivada.

Por supuesto, es esencial que el subobjeto de la clase base se inicialicc correctamente, y slo hay una forma de garantizar esto; realizar la inicializacin en el constructor invocando al constructor de la clase base, que es quien tiene todos los conocimientos y lodos los privilegios para llevar a cabo adecuadamente la inicializacin de la clase base. Java inserta automticamente llamadas al constructor de la clase base dentro del constructor de la clase derivada. El siguiente ejemplo muestra este mecanismo en accin con tres niveles de herencia //: reusing/Cartcon.java // Llamadas a constructores durante la herencia. import static net.mindview.til.Print. class Art { Art(; ( print("Art constructor"); )

7 Reutilizacin de clases 404 } class Drawing extends Art { Drawlng(} { print("Drawing constructor"); }

} public class Cartoon extends Drawing ( public CartoonO { print<"Cartoon constructor"); } public static void mainStringU args) ( Cartoon x = new Cartoon ()

) ) / Output: Art constructor Drawing constructor Cartoon constructor *///:-

Como puede ver. la construccin tiene lugar desde la base hacia afuera', por lo que la clase base se iniciaiiza antes de que los constructores de la clase derivada puedan acceder a ella Incluso aunque no creramos un constructor para Cartoon( ). el compilador sintetizara un constructor predeterminado que invocara al constructor de la clase base.

7 Reutilizacin de clases 405 Ejercicio 3:(2) Demuestre la afirmacin anterior.

Ejercicio 4: (2) Demuestre que los constructores de la clase base (a) se invocan siempre y (b) se invocan antes que los

constructores de la clase derivada.

Ejercicio 5: (I) Cree dos clases, A y B. con constructores predeterminados (listas de argumentos vacias) que impriman

un mensaje informando de la construccin de cada objeto. Cree una nueva clase llamada C que herede de A, y cree un miembro de la clase B dentro ile C. No cree un constructor para C. Cree un objeto de la clase C y observe los resultados. Constructores con argumentos

7 Reutilizacin de clases 406 El ejemplo anterior tiene constructores predeterminados; es decir, que no tienen argumentos. Resulta fcil para el compilador invocar estos constructores, porque no existe ninguna duda acerca de qu argumentos hay que pasar. Si no existe un constructor predeterminado en la clase base, o si se quiere invocar un constructor de la clase base que tenga argumentos, ser necesario escribir explcitamente una llamada al constructor de la clase base utilizando la palabra clave super y la lista de argumentos apropiada: //: reusing/Chess.java // Herencia, constructores y argumentos, import static net.mindview.til.Print.*; class Game ( Gamelint i) ( print I "Game constructor'*} ;

} class BoardGame extends Game { BoardGametint i) ( super(i); orint("BoardGame constructor")j

} i public class Chess extends BoardGame (

7 Reutilizacin de clases 407 ChessO ( super 111); printI"Chess constructor* )

) public static void mam (String n args) ( Chess x = new ChessO/

) | / Output: Game constructor SoardGame constructor Chess constructor ///:

Si no se invoca el constructor de la clase base en BoardCame( ). el compilador se quejar de que no puede localizar un

constructor de la forma Came( ). Adems, la llamada al constructor de la clase base debe ser la primera cosa que se haga

7 Reutilizacin de clases 408 en el constructor de la clase derivada (el compilador se encargar de recordrselo si se olvida de ello)

Ejercicio 6: (I) Utilizando Chess.ja> a. demuestre las afirmaciones del prrafo anterior.

Ejercicio 7: (I) Modifique el Ejercicio 5 de modo que A y B tengan constructores con argumentos en lugar deconstructores predeterminados. Escriba un constructor para C que realice toda la micializacion dentrodel cons

tructor de C.

Ejercicio 8: (I} Cree una clase ba.se que slo tenga un constructor no predeterminado y una clase derivada que tenga

7 Reutilizacin de clases 409 un constructor predeterminado (sin argumentos) y otro no predeterminado En los constructores de la clase derivada, invoque al constructor de la clase base.

Ejercicio 9: (2) Cree una clase denominada Roof que contenga una instancia de cada una de las siguientes clases (que

tambin deber crear): Componen! 1. Componen!! y Componente Derive una clase Stem de Roo! que tambin contenga una instancia de cada componente**. Todas las clases deben tener constructores predeterminados que impriman un mensaje relativo a la clase.

Ejercicio 10: (I) Modifique el ejercicio anterior de modo que cada clase slo tenga constructores no predetemunados Delegacin

Una tercera relacin, que no est directamente soportada en Java, se denomina delegacin. Se encuentra a caballo entre la herencia y la composicin, por que lo que hacemos es incluir un objeto miembro en la clase que estemos construyendo (como en la composicin), pero al mismo tiempo exponemos lodos los mtodos del objeto miembro en nuestra nueva clase (como en la herencia). Por ejemplo, una nave espacial (spaceship) necesita un mdulo de control:

7 Reutilizacin de clases 410 //: reusing/SpaceShipControls. java public class SpaceShipControls ( void upint velocity) () void downtint velocity) (} void Ieft(nt velocity) () void rightint velocity) {) void forwardlint velocity) {) void backmt velocity) () void turboBoost() ()

) ///i-

Una forma de construir la nave espacial consistira en emplear el mecanismo de herencia: //: reusing/SpaceShip.java public class SpaceShip extends SpaceShipControls { private String ame; public SpaceShipfString namel { this.name = ame; } public String toStringO ( retum ame? } public static void main .1 String[] argsl ( SpaceShip protector * new SpaceShip("N5EA Protector"); protector.forward100)/

7 Reutilizacin de clases 411 I

) ///:-

Sin embargo, an objeto spaceship no es realmente "un upu de" objeto SpaccShlpControIs. atm cuando podamos, por ejemplo, decirle" al objeto SpaceShip que avance hacia adelante (foroard< )), Resulta nuts preciso decir que la nave espacial (el objeto SpaceShip) contiene un modulo de control (objeto SpaecSblpControb). y que. al mismo tiempo, todos los mtodos de SpaceShipC'ontrols deben quedar expuestos en el objeto SpaceShip. El mecanismo de delegacin permite resolver este dilema: //; reusing/SpaceShipDelegation. -java public class SpaceShipDelegation ( private String ame; private SpaceShipControls controls =new SpaceShipControlsi}; public SpaceShipDelegation iString name) ( this, name = name;

i // Mtodos delegados: public void back(int velocity) ( Controls.back(velocity);

7 Reutilizacin de clases 412 } public void down(int velocity/ ( controls.down(velocity) ;

) public void forward tint velocity) { controls.forward(velocity);

) public void left(int velocity) { controls.left(velocity);

) public void right(int velocity) ( controls.right(velocity);

) public void turboBoostO { controls.turboBoost() ;

7 Reutilizacin de clases 413 ) public void up(int velocity) ( controls.up(velocity) ; i public static void main(String[] argsJ ( SpaceShipDelegation protector new SpaceShipDelegation<WNSEA Protector*); protector.forward(1001;

) ///:-

Como puede ver los mtodos se dirigen hacia el mtodo controls subyacente, y la interfaz es, por tanto, la misma que con la herencia. Sin embargo, tenemos ms control con la delegacin, ya que podemos decidir proporcionar nicamente un sub- conjunto de los mtodos contenidos en el objeto miembro.

Aunque el lenguaje Java no soporta directamente la delegacin, las herramientas de desarrollo si que

7 Reutilizacin de clases 414 suelen hacerlo. Por ejemplo, el ejemplo anterior se ha generado automticamente utilizando el entorno integrado de desarrollo JetBrains Idea.

7 Reutilizacion de clases 415 I Ejercicio 11: (3) Modifique Detergent.java de modo que utilice el mecanismo de delegacinCombinacin de la composicin y de la herencia

Resulta bastante comn utilizar los mecanismos de composicin y tie herencia conjuntamente F.l siguiente ejemplo muestra la creacin de una clase ms compleja utilizando tanto la herencia como la composicin, junto con la necesaria iniciali- zacion mediante los constructores // reusing/PlaceSetting.java Combinacin de ia composicin y la herencia, impart static net. mmdview. uti 1 .Print. * ; clans Plate ( Plate l int i) ( ) ) print 1"Plate constructor" ) i

clase DinnerFlate extends Plate { DinnerPlate I int i) { super(1); print i"DinnerPlate constructor");

) 1 class tensil { UtensilUnt i) { print("Utensil constructor");

7 Reutilizacion de clases 416 I )

} class Spoon extends Utensil ( Spoon(int i) { super(i) print("Spoon constructor");

i i class Fork extends Utensil ( Fork(int 1) ( super(i) i print("Fork constructor");

i class Knife extends Utensil (

7 Reutilizacion de clases 417 I Knife(int i) ( super(i); printC'Knife constructor");

) i // Una forma cultural de hacer algo: class Custom ( Custom(int i) ( print("Custom constructor");

) public class PlaceSetting extends Custom { private Spoon sp; private Pork frk; prvate Knife kn; prvate DinnerPlate pl; public PlaceSetting(int i) { super(i * 1) ,* sp = new Spoon (i -+ 2 ) ; frk * new Forkti * 3) r kn = new Knife (i + 4> ; pl = new DinnerPlate(i + 5); prlnt<"PlaceSettina constructor"!;

7 Reutilizacion de clases 418 I > public static void maintStrngi] args] ( i PlaceSetting x = new PlaceSetting(9);

) / Output: Custom constructor tensil constructor Spoon constructor tensil constructor Fork constructor tensil constructor Knife constructor Pate constructor DinnerPlate constructor PlaceSetting constructor ///=-

Aunque el compilador nos obliga a iniciali/ar la clase base y requiere que lo hagamos al principio del constructor, no se asegura de que imcialicemos los objetos miembro, asi que es preciso prestar atencin a este detalle.

Resulta asombrosa la forma tan limpia en que las clases quedan separadas. No es necesario siquiera disponer del cdigo fuente de los mtodos paru poder reutilizar ese cdigo. Corno mucho, nos basta con limitamos a importar un paquete (esto es cierto tanto para la herencia como para la composicin) Cmo garantizar una limpieza apropiada

Java no tiene el concepto C-**- de destructor; que es un mtodo que se invoca automticamente cuando se destruye un objeto. La razn es. probablemente, que en Java la prctica habitual es olvidarse

7 Reutilizacion de clases 419 I de los objetos en lugar de destruirlos, permitiendo al depurador de memoria reclamar la memoria cuando sea necesario

A menudo, esto resulta suficiente, pero hay veces en que la clase puede realizar determinadas actividades durante su tiempo de vida que obligan a realizar la limpieza. Como hemos mencionado en el Capitulo 5. mcializocumy limpieza, no podemos saber cundo va a ser invocado el depurador de memoria, o ni siquiera si va a ser invocado. Por tanto, s queremos limpiar algo concreto relacionado con una clase, deberemos escribir explicitamente un mtodo especial para hacerlo y aseguramos de que el programador de clientes sepa que debe invocar dicho mtodo. Adems, como se describe en el Captulo 12. Tratamiento de errores con excepciones, debemos protegemos frente a la aceleracin de posibles excepciones incorporando dicha actividad de limpieza en una clusula finall>

Considere un ejemplo de un sistema de diseo asistido por computadora que dibuje imgenes en la pantalla. //: reusing/CADSystem.java // Cmo garantizar una limpieza adecuada. package reus i ng; import static net.mindview.utii.Print. / class Shape ( Shapemt i) ( print (Shape constructorJ / ) void dispose() ( print("Shape dispose"); )class Circle extends Shape { Circle (int i) { super(i); print("Drawing Circle)/ void dispose() ( print("Erasing Circle"); super.dispose(J;

7 Reutilizacion de clases 420 I )

} class Triangle extends Shape { Triangle<int i) { super(i) ; print("Drawing Triangle")/

) void dispose!) ( print("Erasing Triangle")/ super.dispose()/

) class Line extends Shape ( private int start, end; Line(int start, int end) { super(start);

7 Reutilizacion de clases 421 I this.start = start; this.end = end/ print ("Drawing Line; " * start * ", " 4 end)/

) void dispose() ( print ("Erasing Line: " * start * ", H end); super.di spose()/

) public class CADSystem extends Shape { private Circle C; private Triangle t; private Line[J lines = new Line[3l ; public CADSystem(int i) ( super li 1) ; for (int j = 0; j < lines .length; j-t-t) lines[j] = new Lineij, j*}); c * new Circled); t = new Triangle (Imprint i"Combined constructor");

7 Reutilizacion de clases 422 I public void dispose() { print("CADSystem.dispose()"); //El orden de limpieza es el inverso // al orden de inicializacin: t.dispose\); c.dispose(J; foriint i * lines, length - 1/ i >* 0; i) lines[i].dispose()/ super.dispose( ) ;

} public static void main(StringU args) {CADSystem x - new CADSystem(47)j try ( // Cdigo y tratamiento de excepciones... ) } finally { x.dsposeO j )

) / Output: Shape constructor Shape constructor Drawing Line: 0. 0 Shape constructor Drawing Lne: 1, 1 Shape constructor Drawir.g Line: 2, 4 Shape constructor Drawing Circle Shape constructor Drawing Triangie Combined constructor CADSystem.dispo se() Srasing Triangie Shape d.soose Erasing Circle Simpe

7 Reutilizacion de clases 423 I dlspose Krasmg Line: 2. 4 Shape dispose Erasing Line: 1. 1 Shape dispose Erasing Line: 0, 0 Shape dispoae Shape dispose ///i-

Todo en este sistema es algn tipo de objeto Shape (que a su vez es un tipo de Object. puesto que hereda implcitamente de la clase raz). Cada clase sustituye el mtodo dispose( ) de Shape, adems de invocar la versin de dicho mtodo de la clase base utilizando super Las clases Shape especficas, Circle. Triangie y Line tienen constructores que dibujan las formas geomtricas correspondientes, aunque cualquier mtodo que se invoque durante la vida del objeto puede llegar a ser responsable de hacer algo que luego requiera una cierta tarea de limpieza. Cada clase tiene su propio mtodo dispose( ) pora restaurar todas esas cosas que no estn relacionadas con la memoria y dejarlas en el estado en que estaban antes de que el objeto se creara.

En main( ). hay do?, palabras clave que no habamos visto antes y que no van a explicarse en detalle hasta el Captulo 12, Tratamiento Je errores mediante excepciones: try y finall> La palabra clave try indica que el bloque situado a continuacin suyo (delimitado por llaves) es una regin protegida, lo que quiere decir que se la otorga un tratamiento especial. Uno de estos tratamientos especiales consiste en que el cdigo de la clusula finally situada a continuacin deesia regin protegida siempre se ejecuta, independientemente de cmo se salga de bloque try (con el tratamiento de excepciones, es posible salir de un bloque try de diversas formas distintas de la normal). Aqu, la clusula flnall\ dice: Llama siempre a disposei ) para \. independientemente de lo que suceda.

7 Reutilizacion de clases 424 I En el mtodo de limpieza, (dispose( ). en este caso), tambin hay que prestar atencin al orden de llamada de los mtodos de limpieza de la clase base y de los objetos miembro, en caso de que un subobjeto dependa de otro. En general, hay que seguir la misma forma que imponen los compiladores de G++ para los destructores: primero hay que realizar toda la tarea de limpieza especifica de la clase, en orden inverso a su creacin (en general, esto requiere que los elenentos de la clase base sigan siendo viables) A continuacin, se invoca el mtodo de limpieza de la clase base, como se ilustra en el ejemplo. May muchos casos en los que el tema de la limpieza no constituye un problema, bastando con dejar que el depurador de memoria realice su tarea Pero cuando hay que llevar a cabo una limpieza explcita se requieren grandes dosis de diligencia y atencin, porque el depurador de memoria no sirv e de gran ayuda en este aspecto. El depurador de memoria puede que no

7 Reutilizacin de clases 425 llegue nunca a ser invocado y. en caso de que lo sea, podra reclamar los objetos en el orden que quisiera. No podemos confiar en la depuracin de memoria para nada que no sea reclamar las zonas de memoria no utilizadas Si queremos que las tareas de limpieza se lleven a cabo, es necesario definir nuestros propios mtodos de limpieza y no emplear finaliz ).

Ejercicio 12: (3)Aadauna jerarqua adecuada de mtodos dispose( ) a todas las clases del Ejercicio 9 Ocultacin de nombres

Si una clave base de Java tiene un nombre de mtodo varias veces sobrecargado, redefinir dicho nombre de mtodo en la clase derivada no ocultar ninguna de las versiones de la clase base (a diferencia de lo que sucede en C++). Por tanto, el mecanismo de sobrecarga funciona independientemente de si el mtodo ha sido definido en este nivel o en una clase base: / / : retsing/Hi de. j 3va . La sobrecarga de un nombre de mtodo de la clase base en una i clase derivada no oculta las versiones de la ciase base, import static net .mindview.utii .Print. .Tase Homer ( char doh(char el { print("doh(char)"); rsturn * d * ,* J float dohlfloat f) ( print(*doh(f loat)*/ return l.Ofj

7 Reutilizacin de clases 426 i class Mllhouse (] class Bart extends Homer ( veid dohi'Mllhouse m) { print "doh(Milhcusel") j

) i public class Hide ( public static void nainStringH args1 ( Bart b = new Bart f)/ b.doh(1); b.dnh('x')/ b.dohU .Of) i b.doh(new Milhcuse t);

| /* Oufcput: donlfloat) doh(char) dohlfloat) doh(Mllhouse)

///

7 Reutilizacin de clases 427 Podemos ver que todos los mtodos sobrecargados de Homer estn disponibles en Bar, aunque Bart introduce un nuevo mtodo sobrecargado (si se hiciera esto en G++ se ocultaran los mtodos de la clase base). Como veremos en el siguiente captulo, lo ms comn es sobrecargar los mtodos del mismo nombre, utilizando exactamente la misma signatura y el mismo upo de retorno de la clase de retorno En caso contrario, el cdigo podra resultar confuso (lo cual es la razn por la que C^+ oculta todos los mtodos de la clase base, para que no cometamos lo que muy probablemente se trata de un error).

Java SE5 ha aadido al lenguaje la anotacin (q Ovcrride, que no es una palabra clave pero puede usarse como si lo fuera. Cuando queremos sustituir un mtodo, podemos aadir esta anotacin y el compilador generara un mensaje de error si sobrecargamos accidentalmente el mtodo en lugar de sustituirlo //: reusing/Lisa.java // (CompileTimeError( (Won't compile) class Lisa extends Homer { Override void doh(Mllhouse m) { System.out.printInf"doh(Milhouse) * ) : ) } tu-.-

F.l maleador {CompeTimeErnir) excluye el archivo del proceso de consiruccin con Ant de este libro, pero si lo compila a mano podr ver el mensaje de error: method does not override a method from its superclass

La anotacin (a Override evitara, asi. que sobrecarguemos accidentalmente un mtodo cuando no es

7 Reutilizacin de clases 428 eso lo que queremos hacer.

Ejercicio 13: (2) Cree una clase con un mtodo que est sobrecargado tres veces. Defina una nueva clase que herede de

la anterior y aada una nueva versin sobrecargada del mtodo. Muestre que los cuatro mtodos estn disponibles en la clase derivada. Cmo elegir entre la composicin y la herencia

Tanto la composicin como la herencia nos permiten incluir subobjetos dentro de una nueva clase (la composicin lo hace de forma explcita, mientras que en el caso de la herencia esto se hace de forma implcita). Puede que se est preguntando cul es la diferencia entre ambos mecanismos y cundo conviene elegir entre uno y otro.

Generalmente, la composicin se usa cuando se desea incorporar la funcionalidad de la clase existente dentro de la nueva clase pero no su interfaz En otras palabras, lo que hacemos es integrar un objeto para poderlo utilizar con el fin de poder implementar ciertas caractersticas en la nueva clase, pero el usuario de la nueva clase ver la interfaz que hayamos definido para la nueva clase en lugar de la

7 Reutilizacin de clases 429 interfaz del objeto incrustado. Para conseguir este efecto, lo que hacemos es integrar objetos prvate de clases existentes dentro de la nueva clase.

Algunas veces, tiene sentido permitir que el usuario de la elase acceda directamente a la composicin de esa nueva clase, es decir, hacer que los objetos miembros sean pblicos. Los objetos miembros utilizan la tcnica de la ocultacin de la mplementaein por si mismos, asi que no existe ningn nesgo. Cuando el usuario sabe que estamos ensamblando un conjunto de elementos, normalmente, puede comprender mejor la interfaz que hayamos definido. IJn ejemplo seria un objeto car (coche): //: reusmg/Car.java // Composicin con objetos pblicos. class Engine ( public void start) () public void rev() () public void stop() {)

) class Wheei { public void nflate(int psa) {}

) class Window { public void rollupt) {} public void rolldownO ()

7 Reutilizacin de clases 430 } class Door { public Window window = new Window(); public void oper* () ()public void cise O (}

) public class Car { public Engine engme new Engine); public Wheel(] wheel new Wheel[4! ; public Door left = new DoorU, right = new DoorO? // 2-door public CarO { for(mt i 0; i < 4; i++) wheel[i] = new Wheel();

) public static void mam(StringU args> { Car car = new Car(); car.left.window.rollup); car.wheel[0].nflate 172);

i I ///:-

Puesto que en este caso la composicin de un coche forma parte del anlisis del problema (y no

7 Reutilizacin de clases 431 simplemente del diseo subyacente). hacer los miembros pblicos ayuda al programador de clientes a entender cmo utili/ar la clase, y disminuye tambin la complejidad del cdigo que el creador de la clase tiene que desarrollar. Sin embargo, tenga en cuenta que ste es un caso especial y que, en general, los campos deberan ser privados.

Cuando recurrimos al mecanismos de herencia, lo que hacemos es tomar una clase existente y definir una versin especial de la misma. Fn general, esto quiere decir que estaremos tomando una clase de propsito general y especializndola para una necesidad concreta. Si reflexionamos un poco acerca de ello, podremos ver que no tendra ningn sentido componer un coche utilizando un objeto vehculo, ya que un coche no contiene vehculo, sino que ex un vehculo. La relacin es-un se expresa mediante la herencia, mientras que la relacin tiene-un se expresa mediante la composicin.

Ejercicio 14: (I) En Car. ja va aada un mtodo servicef) a Engine c invoque este mtodo desde main( ). protected

Ahora que ya hemos tomado contacto con la herencia, vemos que la palabra clave protected adquiere su pleno significado. En un mundo ideal, la palabra clave prvate resultara suficiente, pero en los proyectos reales, hay veces en las que queremos ocultar algo a ojos del mundo pero permitir que accedan a ese algo los miembros de las clases derivadas.

7 Reutilizacin de clases 432 La palabra clave protected es homenaje al pragmatismo, lo que dice es: liste elemento es privado en lo que respecta al usuario de la clase, pero est disponible para cualquiera que herede tic esta clase y para todo lo dems que se encuentre en el mismo paquete (protected tambin proporciona acceso de paquete)*.

Aunque es posible crear campos de tipo protected (protegidos), lo mejor es definir los campos como privados; esto nos permitir conservar siempre el derecho a modificar la implcmentacin subyacente. Entonces, podemos permitir que los herederos de nuestra clase dispongan de un mtodo controlado utilizando mtodos protected: //: reusing/Orc.}ava // La palabra clave protected. import static net.mindview.util.Print.; class Villain ( prvate String ame; protected void set (String nmj ( ame * nm; ) public VillainIString namei ( this.name = ame; } public String toStringO ( retum HI*m a Villain and my ame is M ame;

) public class Ore extends Villain ( prvate int orcNumber; public Ore(String ame, int orcNumber) ( Ruperinamel; this.orcNumber - orcNuinber; i

7 Reutilizacin de clases 433 public void chanaeiStrina ame, int orcNuraberi j Retname}; // Disponible porque est protegido. thla.orcNumber * orcNumber,-

) public String toStringO ( return "Ore " * orcNumber i public static void main (String [] argsi ( Ore ore = new Ore("Limburger", 12 )j print(ore; ere.change("Bo b", 19) ; print(ore)j super.toString (J ;

) ) /* Output: Ore 12: Xm a Villain and my ame is Limburger Ore 19; T'm a Villain and my ame is Bob *///:-

Puede ver que change( ) tiene a cccso a sct( ) porque es de tipo protccted. Observe tambin la forma

7 Reutilizacin de clases 434 en que se ha definido el mtodo toStrng( ) de Ore en trminos de toString( ) de la clase base.

Ejercicio 15: (2) Cree una clase dentro de un paquete. Esa clase debe estar dentro de un paquete. Esa clase debe conte

ner un mtodo protccted. Fuera del paquete, trate de invocar el mtodo protected y explique los resulta dos. Ahora defina otra clase que herede de la anterior c invoque el mtodo protected desde un mtodo de la clase derivada.

Upcasting (generalizacin)

El aspecto ms importante de la herencia no es que proporciona mtodos para la nueva clase, sino la relacin expresada entre la nueva clase y la clase base. Esta relacin puede resumirse diciendo que la nueva clase car un tipo de la clase existente.

Esta descripcin no es simplemente una forma elegante de explicar la herencia, sino que est soportada directamente por el lenguaje. Por ejemplo, considere una clase base denominada Instrument que represente instrumentos musicales y una clase derivada denominada YVind (instrumentos de viento) Puesto que la herencia garantiza que todos los mlodos de la clase base estn

7 Reutilizacin de clases 435 disponibles tambin en la clase derivada, cualquier mensaje que enviemos a la clase base puede enviarse tambin a la clase derivada. Si la clase Instrument tiene un mtodo play( ) (tocar el instrumento), tambin lo tendrn los instrumentos de la clase VVind. Esto significa que podemos decir con propiedad que un objeto Wind es tambin un objeto de tipo Instrument. El siguiente ejemplo ilustra cmo soporta el compilador esta idea. //: reusing/Wmd. java // Herencia y generalizacin. class Instrument ( public void playl) {) static void tune(Instrument i) ( // . . .
i.

playO ;

) // Los objetos instrumentos de viento // porque tienen la misma interfaz

7 Reutilizarin de clases 436 :public ciass Wind extends Instrument ( pubiic static void main[String U argsj ( Wind flute = new WindO; Instrument.tune(flutel; // Generalizacin

} ///:-

Lo ms interesante de este ejemplo es el mtodo tune ) (afinar), que acepta una referencia a un objeto Instrument. Sin embargo, en YVind muin() al mtodo tune( ) se le entrega una referencia a un objeto W ind Dado que Java es muy estricto en lo que respecta a las comprobaciones de tipos, parece extrao que un mtodo que acepta un determinado tipo pueda aceptar tambin otro tipo distinto, hasta que nos demos cuenta de que un objeto Wind tambin es un objeto Instrument y de que no existe ningn mtodo que tunc( ) pudiera invocar para un objeto Instrument V que no se encuentre tambin en Wind Dentro de tune(). el cdigo funciona tanto para los objetos Instrument como para cualquier otra cosa derivada de Instrument. y el acto de convertir una referencia a un objeto Wind en otra referencia a Instrument se denomina iipeasting (generalizacin).

7 Reutilizarin de clases 437 Por qu generalizar?

El trmino est basado en la forma en que se vienen dibujando tradicionalmente los diagramas de herencia de clase. Con la raz en la parte superior de la pagina y las clases derivadas distribuyndose hacia abajo (por supuesto, podramos dibujar los diagramas de cualquier otra manera que nos resultara til). El diagrama de herencia para Wind.java sera entonces:

AI realizar una proyeccin de un tipo derivado al tipo base, nos movemos /tocia arriba en el diagrama de herencia, y esa es la razn de que en ingls se utilice el trmino upeasting (up = arriba, casi = proyeccin. El upeasting o generalizacin siempre resulta seguro, porque estamos pasando de un tipo ms especifico a otro ms general. Es decir, la clase derivada es un superconjunto de la clase base. Puede que la clase derivada contenga ms mtodos que la clase base, pero debe contener al menos los mtodos de la clase base. Lo nico que puede ocurrir con la interfaz de la clase durante la generalizacin es que pierda mtodos, no que los gane, y sta es la razn por la que el compilador permite la generalizacin sin efectuar ningn tipo de proyeccin explcita y sin emplear ninguna notacin especial.

Iambin podemos realizar el inverso de l3 generalizacin, que se denomina Jowncasting (especializacin), pero esto lleva asociado un cierto dilema que examinaremos ms en detalle en el siguiente capitulo, y en el Captulo 14, Informacin de tipos.

7 Reutilizarin de clases 438 Nueva comparacin entre la composicin y la herencia

Ln la programacin orientada a objetos, la forma ms habitual de crear y utilizar cdigo consiste en empaquetar los dalos y mtodos en una clase y usar los objetos de dicha clase Tambin utilizamos otras clases existentes para construir nuevas clases utilizando el mecanismo de composicin. Menos frecuentemente, debemos utilizar el mecanismo de herencia. Por tanto, aunque al ensear programacin orientada a objetos se suele hacer un gran hincapi en el tema de la herencia, eso no quiere decir que se la deba usar en todo momento. Por el contrario, conviene emplearla con mesura, y slo cuando est claro que la herencia resulta til. Una de las formas ms claras de determinar si debe utilizarse composicin o herencia consiste en preguntarse si va a ser necesario recurrir en algn momento al mecanismo de generalizacin de la nueva clase a la clase base. Si es necesario usar dicho mecanismo, entonces la herencia ser necesaria, pero si ese mecanismo no hace falta conviene meditar si verdaderamente hay que emplear la herencia. En el Capitulo 8. Polimorfismo se proporciona una de las razones ms importantes para utilizar la generalizacin, pero si se acuerda de preguntarse voy a necesitar generalizar en algn momento?" tendr una buena forma de optar entre la composicin y la herencia.

Ejercicio 16: (2)Creeunaclase defina una nueva clase denomina

denominada Amphihian (anfibio). A partir de sta,

da Frog (rana) que herede de la anterior. Incluya una serie de mtodos apropiados en la clase base. En

7 Reutilizarin de clases 439 niain().cree un objeto Frog y realice una generalizacin a Amphibian. demostrando que todos los mtodos siguen funcionando.

Ejercicio 17: (I) Modifique el Ejercicio I6 para que el objeto Ero sustituya las definiciones de mtodos de la clase

base (proporcione las nuevas definiciones utilizando las mismas signaturas de mtodos). Observe lo que sucede en main( ). La palabra clave final

La palabra cla\c de Java final tiene significados ligeramente diferentes dependiendo del contexto, pero en general quiere decir: Este elemento no puede modificarse". Puede haber dos razones para que no queramos permitir los cambios: diseo Y eficiencia Puesto que estas dos razones son muy diferentes entre s. resulta bastante posible utilizar lu palabra clave final de manera inadecuada.

En las siguientes secciones vamos a ver los tres lugares donde final puede utilizarse: para

7 Reutilizarin de clases 440 los datos, para los mtodos y para las clases. Datos final

Muchos lenguajes de programacin disponen de alguna forma de comunicarle al compilador que un elemento de datos es 'constante". Las constantes son tiles por dos razones:

Puede tratarse de una constante Je tiempo Je compilacin que nunca va a cambiar.


1.

Puede tratarse de un valor inicializado en tiempo de ejecucin que no queremos que cambie.
2.

En el caso de una constante de tiempo de compilacin, el compilador est autorizado a compactar el valor constante en todos aquellos clculos que se le utilice; es decir, el clculo puede realizarse en tiempo de compilacin eliminando asi ciertos clculos en tiempo de ejecucin. En Java, estos tipos de constantes deben ser primitivas y se expresan con la palabra clave final En el momento de definir una de tales constantes, es preciso definir un valor.

7 Reutilizarin de clases 441 Un campo que sea a la vez static y final slo tendr una zona de almacenamiento que no puede nunca ser modificada.

Cuando final se utiliza con referencias a objetos en lugar de con primitivas, el significado puede ser confuso. Con una primitiva. final hace que el valor sea constante, pero con una referencia a objeto lo que final hace constante es la referencia. Lina vez micializada la referencia a un objeto, nunca se la puede cambiar para que apunte a otro objeto. Sin embargo, el propio objeto s que puede ser modificado; Java no proporciona ninguna manera para hacer que el objeto arbitrario sea constante (podemos, sin embargo, escribir nuestras clases de modo que tengan el efecto de que los objetos sean constantes). Esta restriccin incluye a las matrices, que son tambin objetos.

He aqu un ejemplo donde se ilustra el uso de los campos final Observe convenio, los campos que son a

que. por la ve*

static y final (es decir, constantes de tiempo de compilacin) se escriben maysculas, utilizando guiones bajos para sepa

en

rar las palabras.

7 Reutilizarin de clases 442 //: reusmg/FinalData. java // Efecto de final sobre los campos, import java.til.*; import static net.mindview.utii.Print.*; class Valu { int i; // Acceso de paquete public Valu(int i) { this.i = i; }

) public class FinalData { private static Random rand = new P.andomU7) ; prvate String id; public FinalData(String id) ( this.id = id; ) // Pueden ser constantes de tiempo de compilacin: private final int valueOne = 9; prvate static final int VALE__TWQ = 99; // Constante pblica tpica: public scatic final int VALUE_7HREE 1 9 : // No pueden ser constantes de tiempo de compilacin: prvate final int 14 = rand.nextlnt(20) ; static final int 1NT_5 = rand.nextlnt (20',* prvate Valu vi = new Valu(11l; prvate final Valu v2 * new Valu(22); prvate static final Valu VAL_3 = new Valu(33); // Matrices:

7 Reutilizarin de clases 443 prvate final int[] a * ( 1, 2, 3, 4, 5, 6}; public String toStringO ( recum. id " : " + "14 = n + i4 + H. INT_5 * M + INT_5 ;

) public static void main{StringH args) ( FinalData fdl = new FinalData(MfdlM); I I \ fdl.valueOne-M-; // Error: no se puede modificar el valor fdl.v2.i+*; // iEl objeto no es constante! fdl.vi = new Valu(9} j ( / OK -- no es final forlint i * 0; i < fdl.a.length; -m-) fdl.a[i]++; // jEl objeto no es constante! //! Edl.v2 = new Value(O); // Error: no se puede //I fdl.VAL_3 =* new Value(l); // cambiar la referencia //! fdl.a = new int 13j; print(fdl)a print("Creating new FinalData") ; FinalData fd2 = new FinalData("fd2") ; printlfdl); print fd2);

) } I * Output: fdl: 14 15, INT_5 = 10 Creating

7 Reutilizarin de clases 444 new FinalData fdl: 14 15. INT_5 10 fd2: 14 = 13, INT 5 = 10 *///:-

Dado que valueOnc y VALUE_TWO son primitivas final con valores definidos en tiempo de compilacin, ambas pueden usarse como constantes de tiempo de compilacin y no se diferencian en ningn aspecto importante. VAJLUETHREE es la forma ms tpica en que podr ver definidas dichas constantes: public que se pueden usar fuera del paquete, sialie para enfatizar que slo hay una y final para decir que se Irata de una constante. Observe que las primitivas final static con valores iniciales constantes (es decir, constantes en tiempo de compilacin) se designan con letras maysculas por convenio, separando las palabras mediante guiones bajos (al igual que las constantes en C. que es el lenguaje en el que surgi este convenio).

El que algo sea final no implica necesariamente el que su valor se conozca en tiempo de compilacin. El ejemplo ilustra esta inicializando 14 e INT_5 en tiempo de ejecucin, mediante nmeros generados aleatoriamente. Esta parte del ejemplo tambin genera la diferencia entre hacer un valor final esttico o no esttico. Esta diferencia slo se hace patente cuando los valores se inicializan en tiempo de ejecucin, yaque el compilador trata de la misma manera los valores de tiempo de compilacin (en muchas ocasiones, optimizando el cdigo paraeliminar esas constantes). La diferencia se muestra cuando se

7 Reutilizarin de clases 445 ejecuta el programa. Observe que los valores de 4 para fdl y fd2 son distintos, mientras que el valor para INT_5 no cambia porque creemos el segundo objeto FinalData. Esto se debe a que es esttico y se inicializa una sola vez durante la carga

> no

cada vez que se crea un nuevo objeto.

Las variables vi a VAL J ilustran el significado de una referencia final Como puede ver en main( ). el hecho de que v2 *<-*a final no quiere decir que se pueda modificar su valor. Puesto que es una referencia, final significa que no se puede asociar \ 2 con un nuevo objeto. Podemos \a que la afirmacin tambin es cierta para las matrices, que son otro de tipo de referencia (no hay ninguna forma que yo conozca de hacer que las referencias a una matriz sean final). Hacer las referencias final parece menos til que definir las primitivas como final.

Ejercicio 18: (2)Creeunaclasccon la diferencia entre los dos Valores final en blanco

un campo static final y un campo llnal y demuestre

7 Reutilizarin de clases 446 Java permite la creacin de valores finales en blanco, que son campos que se declaran como final pero que no se les proporciona un valor de inicializacin. Fn todos los casos, el valor final en blanco debe ser inicial izado antes de utilizarlo, y el compilador se encargar de hacer que esto sea asi. Sin embargo, los valores final en blanco proporcionan mucha ms flexibilidad en el uso de la palabra clave final ya que, por ejemplo, un campo final dentro de una clasc podr con esto ser diferente para cada objeto y mantener an asi su carcter de inmutable. He aqu un ejemplo: //: reusing/BlankFinal.java ! i Campos final en blanco*. class Poppet { private int i; Poppet(int ii) { i = ii; ) i public class BlankFinal { private final int i 0; // Valor final inicial izado private final int j; // Valor final en blanco prvate final Poppet p; // Referencia final en blanco // Los valores final en blanco DEBEN inicializarse en el constructor: public BlankFinal() ( j =1; // Inicializar valor final en blanco p = new Poooet(1); // Inicializar referencia final en blanco

) public BlankFinal(int x) { j x; // Inicializar valor final en blanco p = new Poppet (x) , // Inicializar referencia final en blanco

7 Reutilizarin de clases 447 ) public static void main(String[J args) ( new BlankFinal(J; new BlankFinal(47);

) ///:-

Estamos obligados a realizar asignaciones a los valores final utilizando una expresin en el punto de definicin del campo

o bien en cada constructor. De esa forma, se garantizar que el campo final est siempre inicializado antes de utilizarlo.

7 Reutilizarin de clases 448 Ejercicio 19: (2) Cree una clase con una referencia final en blanco a un objeto. Realice la inicializacin de la referen

cia final en blanco dentro de todos los constructores. Demuestre que se garantiza que el valor final estar inicializado antes de utilizarlo, y que no se puede modificar una vez inicializado. Argumentos final

Java permite definir argumentos final declarndolos como tales en la lista de argumentos. Esto significa que dentro del mtodo no se podr cambiar aquello a lo que apunte la referencia del argumento: //: reusing/FinalArguments.java // Uso de "final" con argumentos de mtodos. clas9 Gizmo { public void spinO () ) public class FinalArguments ( void with(final Gizmo g) { //! g new Gizmo(); // Ilegal -- g es final

) _:a without teizms 3 {

7 Reutilizarin de clases 449 3 c new Sixmo \;0K - - g r.c es f i n a l 3,spin i 1 voi* f'final ir.t i i+t; ) Xq se puede carx-iar LES primitivas final slo pueden leerse, int 3 final int i> { return i I* | pibli* static void main(String[] args- j Fijiaiargumencs b new FinaiArgument5 'j; b i .without'nuil); L r with nuil ir

Lo* mtodos f( ) \ g( ) muestran lo que sucede cuando ios argumentos primitivos son final: se puede leer el argumento, pero no modificarlo. Esta caracterstica se utiliza principalmente para pasar datos a las clases internas annimas, lo cual es un rema del que hablaremos en el Captulo 10. Clase* memas Mtodos final

Ha> dos razones para utilizar mtodos final La primera es bloquear" el mtodo para impedir que cualquier clase que herede ie esta cambie su significado. Esto se hace por razones de diseo cuando queremos aseguramos de que se retenga el comportamiento de un mtodo durante la herencia y que ese mtodo pueda ser sustituido.

La segunda razn por la que se ha sugerido en el pasado la utilizacin de los mtodos

7 Reutilizarin de clases 450 final es la eficiencia. Fn las imple- men tac iones anteriores de Java, si definamos un mtodo como final, permitamos al compilador convertir todas las llamada' a e>e mtodo en llamadas en linea Cuando el compilador vea una llamada a un mtodo final, podia (a su discrecin) saltarse el modo nonna! de insertar el codigo correspondiente al mecanismos ile llamada al mtodo (insellar los argumentos en la pila, saltar al cdigo del mtodo y ejecutarlo, saltar hacia atrs v eliminar de la pila los argumentos y tratar el valor de retomo), paia sustituir en su lugar la llamada al mtodo por una copia del propio cdigo contenido en el cuerpo del mtodo Esto elimina el gasto adicional de recursos asociado a la llamada al mtodo. Por supuesto, si el mtodo es de gran tan.ao. el cdigo empezar a crecer enormemente y probablemente no detectemos ninguna mejora de velocidad por la utilizacin de mtodos en linea, ya que la mejora ser insignificante comparada con la cantidad de tiempo invertida dentro del metodo,

I it Lis versiones mas recientes de Java, la mquina virtual (en particular, la tecnologa h o t o p o f ) puede detectar estas situaciones y eliminar el paso adicional de imlireecion. por lo que ya no es necesario (de hecho, se desaconseja por regla genera! utilizar final para tratar de ayudar al optimizador Con Java SF5 6. lo que debemos hacer es dejar que el compilador y la J\ VI se encarguen de las cuestiones de eficiencia, y slo debemos definii un metodo como final s queremos impedir explcitamente la sustitucin del mtodo en las clases derivadas.1 final y prvate

l o> mtodos privados de una clase son implicitamente de tipo final (finales). Puesto que no se puede acceder a un metodo privado, es imposible sustituirlo en una clase derivada Podemos aadir el especificador final a un metodo private, pero 110 tendr ningn efecto adicional.

7 Reutilizarin de clases 451 tema puede causar algo du- confusin, porque si se trata de sustituir 1111 metodo private (que implcitamente es final), parece que el mecanismo funciona y el compilador no proporciona ningn mensaje de error.
1 -le

: reusng/FinalOverriding! Ilusin.java Tan slo parece que podamos sustituir / un mtodo privado o un mtodo privado final itrport static net.mindviftw.til.frmt ; \'o pierda el tiempo traame* de optimi/ar prematuramente Si el sistema funciona > es demasiado temo a*utia dudoyo que !i> pueda solventar con la palabra clave final htip Miiultu u wi Books contiene informacin acerca de !,u tcnicas de perfilado, que pueden servir dv ivud;-. a la hora !<- acelerar lo* programa* cass WithFinals {
II

Idntico ai uso de "prvate" sola:

private final void fCJ ( print \ "WithFinals.f(J" I ; ) // Tambin automaaticmente "final1'i private void g{) ( print("WithFinals.gOM); (

) class OverridingPrivate extends WithFinals ( private final void fO j \ print("OverridingPrivate.f{ } " ) ;

private void g() ( print COverridingPrivate.gO ">;

7 Reutilizarin de clases 452 >

) class OverrdingPrivate2 extends OverridingPrivate { public final void f() ( print ("OverridingPnvate2 . f () H) ;

} public void gO { print <"OverridingPrivate^.g()");

) public class FinalOverridinglllusio

7 Reutilizarin de clases 453 n { public static void main(String[] args) { OverridingPrivate2 op2 = new OverridingPrivate2(I ; op2.f(); op2.g () / // Se puede generalizar: OverridingPrivate op = op2; // Pero no se pueden invocar los mtodos: //! op.fO? //! op.gO-f
II

Lo mismo aqu:

WithFinals wf p2; J //! wf.fO // ! wf.g ();

) /* Output: OverridingPnvate2. f ) OverridingPrivate2.g) V//S-

La ''sustitucin de los mtodos slo puede lener lugar si el mtodo forma parte de la clase base. Ln otras palabras, es necesario poder generalizar un objeto a su tipo base y poder invocar al mismo mtodo (este tema resultar ms claro en el siguiente capitulo). Si un mtodo es privado* no forma parte de la interfaz de la clase base. Se Irata simplemente de un cierto cdigo que est oculto dentro de la clase y que sucede que tiene ese nombre, pero si creamos un mtodo public. protected o con acceso de paquete con el mismo nombre en la clase derivada, no habr ninguna conexin con el mtodo que resulte que tiene el mismo nombre en la clase base Es decir, no habremos sustituido el mtodo, sino que simplemente habremos creado oiro nuevo. Puesto que un mtodo private es inalcanzable y resulta invisible a efectos prcticos, a lo nico que afecta es a la organizacin del cdigo de la clase en la que haya sido definido.

7 Reutilizarin de clases 454 Ejercicio 20: (I) Demuestre que la anotacin Override resuelve el problema descrito en esta seccin

Ejercicio 21: (I) Cree una clase con un mtodo final. Cree otra clase que herede de la clase anterior y trate de sustituir ese mtodo

7 Reutilizacin de clases 455 .Clases final

Cuando decimos que iodo una clase es de lipo final (precediendo su definicin con la palabra clave final), lo que estamos diciendo es que no queremos heredar de esta clase ni permitir que nadie ms lo haga. En otras palabras, por alguna razn, el diseo de nuestra clasc es de tal naturaleza que nunca va 3 ser necesario efectuar ningn cambio, o bien no queremos que nadie defina clases derivadas por razones de seguridad. //: reuslng/Jurassic.java // Definicin de una clase completa como final. class SmallBrain (}

final class Dinosaur ( int 1 * 7 ; int j = 1; SmallBrain x * new SmallBrain () ; void f () {)

) //j class Further extends Dinosaur ()


II

error: no se puede heredar de la clase final 'Dinosaur'

public class Jurassic { public static void main(StringU aras) ( Dinosaur n new Dinosaur();

7 Reutilizacin de clases 456 n.f 0 ; n.i 40;

n. j-M-;

) ///:-

Observe que los campos de una clase final pueden ser de tipo final o no, segn deseemos A los campos de tipo final se les aplican las mismas reglas independientemente de si la clase esta definida como final. Sin embargo, como la clasc impide la herencia, todos los mtodos en una clase final son implcitamente final, ya que no existe ninguna forma de sustituirlos. Podemos aadir el especificador final a un mtodo de una clase final, pero no tiene ningn efecto adicional.

Ejercicio 22: (I) Cree una clase final y trate de definir otra clase que herede de ella

7 Reutilizacin de clases 457 Una advertencia sobre final

En ocasiones, uno puede verse tentado a definir un mtodo como final a la hora de definir una clasc. pensando que nadie podra querer sustituir ese mtodo. A veces, es cierto que las cosas pueden ser as.

Pero tenga cuidado con las suposiciones que realiza. En general, es difcil prever cmo se va a reutilizar una clasc. especialmente si se trata de una clase de propsito general. Si define un mtodo como final, puede que este impidiendo rcutilzar la clase a travs del mecanismo de herencia en algn otro proyecto de programacin, simplemente porque no haba llegado a imaginar que esa clase pudiera llegar a emplearse de esa forma.

La biblioteca estndar de Java es un buen ejemplo de esto. En particular. la clase Vector de Java 1.0 1.1 se utilizaba de forma bastante comn y todava pinla haber resultado mas til si. por consideraciones de eficiencia (que no pasaban de ser una ilusin), no se hubieran hecho todos los mtodos final. Resulta fcil imaginar que alguien quiera heredar una clase tan fundamental y tan til y sustituir sus mtodos, pero los diseadores decidieron por alguna razn que esto no era apropiado. Resulta bastante irnico que se tomara esa decisin por dos razones distintas. En pnmer lugar. Stack (pila) hereda de Vector, lo que quiere decir que Stack es un Vector, lo que no es realmente cierto desde un punto vista lgico. En cualquier caso, se trata de un ejemplo en el que los propios diseadores de Jav a decidieron que una determinada clasc heredara de Vector Cuando crearon Stack de esta forma, debieron darse cuenta de que los mtodos final eran bastante restrictivos.

En segundo lugar, muchos de los mtodos ms importantes de Vector, como addElement() y clementAt( ) estn sincronizados (synchronized). Como veremos en el Captulo 21, Concurrencia, esta caracterstica restringe significativ amente las prestaciones, lo que probablemente anula cualquier

7 Reutilizacin de clases 458 ganancia proporcionada por final. Fste ejemplo tiende a avalar la teora de que los programadores suelen equivocarse siempre a la hora de decidir donde hay que optimizar. Resulta un poco penoso que un diseo tan pobre terminara siendo incluido en la biblioteca estndar para que todo el mundo tuviera que sufrirlo (afortunadamente, la moderna biblioteca de contenedores Java sustituye Vector por ArrayList. que se comporta de una forma mucho ms civilizada; lamentablemente, hoy da se sigue escribiendo cdigo que utiliza la antigua biblioteca de contenedores).

Resulta tambin interesante observar que Hashtablc. otra clase importante de la biblioteca estandar 1.0/1.1 de Java no tiene ningn mtodo final Como va se ha mencionado, es patente que algunas de las clases fueran diseadas por personas distintas (tendr la ocasin de comprobar que los nombres de los mtodos en Hashtablc son mucho ms breves comparados con los de Vector, lo que constituye otra prueba de esta afirmacin). Esto es. precisamente, el tipo de cosas que no resultan obvias para los consumidores de una biblioteca de clases. Cuando las cosas no son coherentes, damos ms trabajo del necesario a los usuarios, lo cual es otro argumento en favor de las revisiones de diseo y de los programas (observe que la biblioteca actual de contenedores Java sustituye Hashtable por HashMap). Inicializacin y carga de clases

En los lenguajes ms tradicionales, los programas se cargan de una vez como parte del proceso de arranque. Este proceso va seguido del de inicializacin y luego del programa. El proceso de inicializacin en estos lenguajes debe controlarse cuidadosamente para que el orden de inicializacin de los valores estticos no cause problemas. Por ejemplo, C++ tiene problemas si uno de los valores estticos espera que otro valor esttico sea vlido antes de que el segundo haya sido inicial izado.

Java no tiene este problema porque adopta una tcnica de carga completamente distinta. sta es una de las actividades que se facilitan enormemente porque en Java todo es un objeto. Recuerde que el cdigo compilado de cada clase est almacenado en su propio archivo separado. Dicho archivo no se carga hasta que ese cdigo sea necesario. En general, podemos decir que "el cdigo de las clases se carga en el lugar que por primera vez se utiliza". Usualmentc, dicho lugar es cuando se construye el primer objeto de esa clase, aunque la carga tambin puede tener lugar cuando se acceda a un eampo esttico o a un mtodo esttico. 2

7 Reutilizacin de clases 459 El lugar del primer uso es tambin el lugar donde se produce la inicializacin de los elementos estticos Todos los elementos estticos y el bloque de cdigo static se inicializarn en orden textual (es decir, en el orden en el que estn escritos en la definicin de la clase), en el punto donde se produzca la carga. Por supuesto, los valores estticos slo *e micializan una

vez. inicializacin con herencia

Resulta Util analizar el proceso de inicializacin completo, incluyendo la herencia, para hacerse una idea general Considere el siguiente ejemplo: //: reusing/Beetle.java // El proceso completo de inicializacin. import static net .mindview.til .Print. fclass Insect ( private int i = 9; protected int j; Insectt) ( print ("i * " i + j * * * j) j * 39;

) private static int xl * printInit("static Insect.xl initialized"); static int printlnit(String si {

7 Reutilizacin de clases 460 : El constructor a tambin un mtodo esttico, an cuando la palabra cIhvc ststk no sea explcita. Por tanto, para ser preciso*, una clase se carga por poniera ve; cuando ve accede a cualquiera de su miembros estticos printis); retum 47;

) public class Beetie extends Insect ( private int k = printlnit("Beetle.k intill2edMJ; public Beetiei 1 ] print(Hk = * k) ; printl"J = B 4 j ) ; i prvate static int x2 = printlnit("static Beetie.x2 initialized"); public static void main(StringU args) { print("Beetie constructor"); Beetie b new Beetie W

} } / Output: static Insect.xl initialized static Beetie.x2 initialized Beetie constructor i * 9. j = 0 Beetle.k initialized k 47 j * 39 ///:-

Lo primero que sucede cuando se ejecuta Java con Beetie es que se traa de acceder a Beetle.niain() (un mtodo esttico), por lo que el cargador localiza el cdigo compilado correspondiente a la clase Beetie (en un archivo denominado Beetlc.class) Durante el proceso de carga, el cargador observa que tiene una clase base (eso es lo que dice la palabra clave eUends). por lo que procede a cargarlo. listo suceder independientemente de si se va a construir un objeto de dicha clase base (pruebe a desactivar con comntanos la creacin del objeto como demostracin)

7 Reutilizacin de clases 461 Si la clase base tiene a su vez otra clase base, esa segunda clase base se cargara, y asi sucesivamente. A continuacin, se realiza la inicializacin static en la clase base raz (en este caso. Insect). y luego en la siguiente clase derivada, etc. Esto es importante, porque la inicializacin static de la clase derivada puede depender de que el miembro de la clase base haya sido inicializado adecuadamente.

En este punto, ya se habrn cargado todas las clases necesarias, por lo que se podr crear el objeto. En primer lugar, se asignan los valores predeterminados a todas las primitivas de este objeto y se configuran las referencias a objetos con el valor nuil (esto sucede en una nica pasada, escribiendo ceros binarios en la memoria del objeto). A continuacin, se invoca el constructor de la clase base. En este caso la llamada es automtica, pero tambin podemos especificar la llamada al cons- tmetor de la clasc base (como primera operacin dentro del constructor Beetle( )) utilizando super El constructor de la clase base pasa a travs del mismo proceso y en el mismo orden que el constructor de la clase derivada. Despus de que se complete el constructor de la clase base, las variables de instancia se inicializan en orden textual. Finalmente, se ejecuta el resto del cuerpo del constructor.

Ejercicio 23: (2) Demuestre que el proceso de carga de una clase slo nene lugar una vez. Demuestre que la carga puede

ser provocada por la creacin de la primera instancia de esa clase o por el acceso i un miembro esttico de la misma.

Ejercicio 24: (2) En Beetle.java. defina una nueva clasc que represente un tipo especifico de la clase Beetie de la que de

7 Reutilizacin de clases 462 be heredar, siguiendo el mismo formato que las clases existentes. Trace y explique los resultados de salida. Resumen

Tanto la herencia como la composicin permiten crear nuevos tipos a partir de los tipos existentes. La composicin rcutt* li/a los tipos existentes como parte de la implementacin subyacente del nuevo tipo, mientras que la herencia reutiliza la interfaz.

Con la herencia, la clase derivada liene la interfaz de la clase base, asi que puede ser generalizada hacia la base, lo cual resulta critico para el polimorfismo, como veremos en el siguiente capitulo.

A pesar del gran nfasis que se pone en las cuestiones de herencia cuando hablamos de programacin orientada a objetos, al comenzar un diseo suele ser preferible la composicin (o preferiblemente la delegacin) en una primera aproximacin, y utilizar la herencia slo cuando sea claramente necesario. La composicin tiende a ser ms flexible. Adems, utilizando la herencia como artificio aadido a un tipo que se haya incluido como miembro de una clase, se puede modificar el tipo exacto (y por tanto el comportamiento) de esos objetos miembro en tiempo de ejecucin. De este modo, se puede modificar el comportamiento del objeto compuesto en tiempo de ejecucin.

A la hora de disear un sistema, nuestro objetivo consiste en localizar o crear un conjunto de clases en el que cada clase tenga un uso especifico y no sea ni demasiado grande (que abarque lauta funcionalidad que sea difcil de reuttlizar) tu incmodamente pequea (de modo que no se pueda emplear por si misma o sin aadirla funcionalidad). Cuando los diseos comienzan a complicarse

7 Reutilizacin de clases 463 demasiado, suele resultar til aadir ms objetos descomponiendo los existentes en otros mas pequeos.

Cuando se proponga disear un sistema, es importante tener en cuenta que el desarrollo de los programas es un proceso incremental, al igual que el proceso de aprendizaje humano. El proceso de diseo necesita de la experimentacin; podemos hacer las tareas de anlisis que queramos pero es imposible que lleguemos a conocer todas las respuestas en el momento de arrancar el proyecto. Tendr mucho ms xito (y podr probar las cosas antes) si comienza a hacer crecer el proyecto como una criatura orgnica en constante evolucin, en lugar de construir todo de una vez. como si fuera un rascacielos de cristal La herencia y la composicin son dos de las herramientas ms fundamentales en la programacin orientada a objetos a la hora de realizar tales experimentos en el curso de un proyecto.

Puede enconirnr las soluciones n los ejercicios seleccionados en el documento electrnico The Thinking in Java Annotated Solution Guide, disponible para la \ cilia cn 'MlMindVie^.wi.Polimorfismo

8 *En ocasiones me he preguntado. Sr. Babbage, si introduce en la mquina datos errneos, podr suministrar las respuestas correctas? Debo confesar que no soy capaz de comprender qu tipo de confusin de ideas puede hacer que alguien plantee semejante pregunta*'. Charles Babbage (17911871)

Fl polimorfismo es la tercera de las caractersticas esenciales de un lenguaje de programacin orientado a objetos, despus de la abstraccin de datos y de la herencia.

Proporciona otra dimensin de separacin entre la interfaz y la implemcntacin. con el tin de desacoplar el qu con respecto al cmo. El polimorfismo permite mejorar la organizacin y la legibilidad de cdigo, as como crear programas amplia- bles que puedan hacerse crecer no slo durante el proceso original de desarrollo del proyecto, sino tambin cada vez que se desean adherir nuevas caractersticas.

El mecanismo de cncapsulacin crea nuevos tipos de datos combinando diversas caractersticas y comportamientos. La tcnica de la ocultacin de la implemcntacin separa la interfaz de la implementacin haciendo que los detalles sean de tipo privado. Este tipo de organizacin mecnica tiene bastante sentido para las personas que tengan experiencia con la programacin procedimental. Pero el polimorfismo trata la cuestin del acoplamiento en trminos de tipos. En el ltimo capitulo, hemos visto que la herencia permite tratar un objeto como si fuera de su propio tipo o como si fuera del tipo base. Esta capacidad resulta critica, porque permite tratar varios tipos (todos ellos derivados del mismo tipo base) como si fueran un nico tipo, pudindose utilizar un mismo fragmento de cdigo para procesar de la misma manera todos esos tipos diferentes La llamada a un mtodo polimrfico permite que cada tipo exprese su distincin con respecto a los otros

tipo> similares siempre y cuando ambos deriven del mismo tipo base. Esta distincin se expresa mediante diferencias en el comportamiento de los mtodos que se pueden invocar .1 travs de la clase base.

En este capitulo, vamos a estudiar el tema del polimorfismo (tambin denominado acoplamiento dinmico o acoplamiento tardo o acoplamiento en tiempo de ejecucin) comenzando por los conceptos ms bsicos y proporcionando ejemplos simples en los que nos fijremos tan slo en el comportamiento polimrfico de los programas Nuevas consideraciones sobre la generalizacin

En el ltimo captulo hemos visto cmo puede utilizarse un objeto como si fuera de su propio tipo o como si fuera un objeto del tipo base. El acto de tomar una referencia a un objeto y tratarla como si fuera una referencia a su tipo base se denomina generalizacin (upeasting) debido a la forma en que se dibujan los rboles de herencia, en los que la clase base se suele representar en la parte superior.

lambin vimos en el capitulo anterior cmo surgi el problema a este respecto, el cual se ilustra en el siguiente ejemplo sobre instrumentos musicales.

En pnmer lugar, puesto que en muchos de estos ejemplos los instrumentos hacen sonar notas (Note), vamos a crear una enumeracin separada Note, dentro de un paquete: //: polymorphism/music/Note.java // Notas para tocar en los instrumentos musicales.

package polymcrphiflm.music;

r public enum Note ( MIDDI.E_C. C_SHASP, S_FLAT; // Etc.

) ///=-

Los tipos en u 111 se han presentado en el Capitulo 5. Inicial ilacin i limpieza.

Aqu Wind es un tipo de Instrument; por tanto. ind hereda de Instrument: i ( t palyrnorphism/music/Inscniineat .java

package polymorphism.music; import static net.mindview.util.Print.; class Instrument { public void play(Note n) { print ("Instrument ,play{)M};

>

) tu-//; polymorphism/music/Wind.java package polymorphism.music/ // Los objetos Wind son instrumentos // porque tienen la misma interfaz; public class Wind extenas Instrument { // Redefinicin de un mtodo de la interfaz: public void play(Note n> {

System.out.printIn("Wind .play() + n);

} m-.I I \ poiymorphism/music/Music.java // Herencia y generalizacin, package polymorphism.music; public class Music { public static void tuneInstrument i) ( U ,,,

i.playlNote.MIDDLE_CI i

public static void main(String[] srgs> ( Wind flute * new Wind{); tunellute); // Generalizacin

! } /* Output: Wind.playO MIDDLE_C *///-

El mtodo Musc.tune< ) acepta una referencia a Instrument, pero tambin a cualquier cosa que se derive de Instrument. Podemos ver que esto sucede en main( ), donde se pasa una referencia W nd a tune( ). sin que sea necesario efectuar ninguna proyeccin. Esto resulta perfectamente lgico: la interfaz de Instrument debe existir en Wind. porque W ind hereda de Instrument. I.a generalizacin de W ind a Instrument puede estrechar" dicha interfaz, pero en ningn caso esa interfaz podr llegar a ser ms pequea que la interfaz completa de Instrument Por qu olvidar el tipo de un objeto

Music.java puede resultarle un poco extrao. Por qu alguien debera olvidar intencionadamente el tipo de un objeto? Esto es lo que sucede cuando efectuamos una generalizacin, y parece que seria mucho ms sencillo si tunet) simplemente tomar una referencia a Wind como argumento. Esto plantea un punto esencial %i hiciramos eso. necesitaramos escribir un

nuevo mtodo tune( ) para cada clase derivada de Instrument que incluyramos en nuestro sistema. Suponga que siguironla esta forma de razonar y aadiramos dos instrumentos Stringed (instrumentos de cuerda) > Brass (instrumentos de metal): polymorphism/music/Music2 . java Sobrecarga en lugar de generalizacin, package polymorphisnumusic; import static net.mindview.util.Print.; class Stringed extends Instrument { public voxd play(Note n) ( print ("Stringed. pl ay i) * + ni /

i j class Bras3 extends Instrument ( public void playNote n) { orine("Brass.playM " n); ) ) public class Music2 { public static void tune(Wmd i) ( i .play (Note. MIDDLE_C) ; 1 public static void tune(Stringed iJ ( i.play(Note.MIDLE_C); l public static void tune(Brass i) ( x.piay(Note.MIDDLE_C);

) public static void main(StringtJ args) ( Wind flute new Wind O ; Stringed vlolin = new Strlnaedl); Brass frenchHom = new Brass(),* tune(flute); // Sin generalizacin tune(violn);

tune f renchHo m 1 :

} ) / Output: Wind.play( } XIDDLE_C Stringed.playO MIDDLE_C Brass.play(J MIDDLE_C ///:-

Esta solucin funciona, pero presenta una desventaja importante es necesario escribir mtodos especficos del tipo para cada nueva clase derivada de Instrument que aadamos. Esto significa, en primer lugar, un mayor esfuerzo de programacin, pero tambin quiere decir que si queremos aadir un nuevo mtodo como tune( ) o un nuevo tipo de clase derivada de Instrument, el trabajo adicional necesario es considerable. Si a esto le aadimos el hecho de que el compilado- no nos dara ningn mensaje de error si nos olvidamos de sobrecargar alguno de los mtodos, todo el proceso de gestin de los tipos se vuelve inmanejable.

No sera mucho ms fcil, si pudiramos, limitamos a escribir un nico mtodo que tomara la clase base como argumento y no ninguna de las clases derivadas especificas' En otras palabras: no seria mucho ms adecuado si pudiramos olvidamos de que hay clases derivadas y escribir el cdigo de manera que slo se entendiera con la clase base?

Eso es exactamente lo que el polimorfismo nos permite hacer. Sin embargo, la mayora de los programadores que proceden del campo de los lenguajes de programacin proced menta Ies suelen tener problemas a la hora de entender cmo funciona el polimorfismo.

Ejercicio 1: (2) Cree una elase Cycle. con subclases Unicycle. Blcycle y Tricyde. Demuestre que se puede generali

zar una inslancia de cada tipo a Cycle mediante un mtodo ride< ) El secreto

La dificultad con Musfe.java puede verse ejecutando el programa. La salida es \Vnd.play( ). Se trata claramente de la salida deseada, pero no parece tener sentido que el programa funcione de esa forma. Examinemos el mtodo tune( ): pubile static void cune(Xr.scrument i) ( i.play(Note.MIBDLE_C); // ...

El mtodo recibe una referencia a Instruuunt De modo que cmo puede el compilador saber que esta referencia a Instruiiient apunta a un objeto Wind en este caso y no a un objeto Brass o Stringcd'.' El compilador no puede saberlo. Para comprender mejor esta cuestin, resulta til que examinemos el tema del acoplamiento.

Acoplamiento de las llamadas a mtodos

El hecho de conectar una llamada con el cuerpo del mtodo se denomina acoplamiento. Cuando se realiza el acoplamiento antes de ejecutar el programa (es decir, cuando lo realizan el compilador v el montador, si es que existe uno), el proceso se llama acopiamiento temprano (early binding). Puede que haya odo este trmino antes porque en los lenguajes proeedimen- tales, como por ejemplo C'. slo existe un tipo de llamadas a mtodos y ese tipo es precisamente, el acoplamiento temprano. as que no existe ninguna posibilidad de elegir.

La parte confusa del programa anterior es precisamente la que se refiere al acoplamiento temprano, porque el compilador no puede saber cul es el mtodo correcto que hay que llamar cuando slo dispone de una referencia Instruinent.

La solucin es el acoplamiento tarx/io [late binding), que quiere decir que el acoplamiento tiene lugar en tiempo de ejecucin basndose en el tipo del objeto. El acoplamiento tardo tambin se denomina acoplamiento dinmica o acoplamiento en tiempo de ejecucin. Cuando un lenguaje implementa el mecanismo de acoplamiento tardo, debe haber alguna manera de determinar el tipo del objeto en tiempo de ejecucin, con el fui de llamar al mtodo apropiado. En otras palabras, el compilador sigue sin saber cul es el tipo del objeto, pero el mecanismo de invocacin del mtodo lo averigua y llama al cuerpo de mtodo correcto. El mecanismo de acoplamiento tardo vara de un lenguaje a otro, pero podemos considerar que en todos los objetos debe incorporarse una cierta informacin sobre el tipo del objeto.

El mecanismo de acoplamiento de mtodos en Java utiliza el acoplamiento tardo a menos que el mtodo sea esttico o de tipo final los mtodos prvale son implicitamente final). Esto quiere decir que, normalmente, no es necesario tomar ninguna decisin acerca de si debe producirse el acoplamiento tardo, ya que ste tendr lugar automticamente.

<fParu qu quemamos declarar un mtodo como final? Como hemos indicado en el capitulo anterior, esto evita que nadie pueda sustituir dicho mtodo en Las clases derivadas Adems, y todava ms importante, esta palabra clave desactiva en la practica el acoplamiento dinmico, o ms bien le dice al compilador que el acoplamiento dinmico no es necesario. Esto permite que el compilador genere un cdigo ligeramente ms eficiente para las llamadas a mtodos final. Sin embargo, en la mayora de los casos, no ser perceptible la ganancia de velocidad en el programa, por lo que lo mejor es utilizar final nicamente por decisin de diseo, y no como intento de mejorar las prestaciones. Especificacin del comportamiento correcto

Ahora que sabemos que todo el acoplamiento de mtodos en Java tiene lugar polimrficamente a travs del acoplamiento tardo, podemos escribir el codigo de forma que se comunique con la clase base, a sabiendas de que lodos los casos donde estn involucradas las clases derivadas funcionarn correctamente con el mismo cdigo. O. dicho de otro modo, enviamos un mensaje a un objeto y dejamos que el objeto averige qu es lo que tiene que hacer*.

El ejemplo clsico en la programacin orientada a objetos es el de las formas". Se suele utilizar comunmente porque resulta fcil de visualizar, pero lamentablemente puede hacer que los programadores inexpertos piensen que la programacin orientada a objetos slo sirve para la programacin grfica, lo cual, por supuesto, no es cieno.

El ejemplo de las formas tiene una clase base denominada Shape (forma) y varios tipos derivados: Crcle (circulo), Squarc (cuadrado). Triangle (tringulo), etc. La ra/n por la que este ejemplo es tan adecuado es porque es fcil decir un circulo es un tipo de forma" y que el lector lo entienda. El diagrama de herencia muestra las relaciones: La generalizacin puede tener lugar en una instruccin tan simple como la siguiente. Shape a = new Circlet);

Aqu, se crea un objeto Circle, y la referencia resultante se asigna inmediatamente a un objeto Shape, (lo que podra parecer un error asignar un tipo a otro); sin embargo, es perfectamente correcto, porque un objeto Circle vs una forma (Shape) debido a la herencia. Por tanto, el compilador aceptar la instruccin v no generar ningn mensaje de error.

Suponga que invoca uno de los mtodos de la clase base (que han sido sustituidos en las clases derivadas): s.draw{ )

De nuevo, cabra esperar que se invocara el mtodo draw( ) de Shape porque, despus de todo, esto es una referencia a Shape, asi que como podra el compilador hacer cualquier otra cosa*7 Sin embargo, se invoca el mtodo apropiado Circlc.draw() debido al acoplamiento tardo (polimorfismo).

El siguiente ejemplo presenta las formas de una manera ligeramente distinta. En primer lugar, vamos a crear una biblioteca reutih/able de tipos Shape //: polymorphism/shape/Shape.java package polymorphism.shape; public class Shape { public void drawn () public void erase() {]

) ///:/ /: polymorphism/shape/Circie.3ava package polymorphism. shape import static net.mindview.util.Print.; public class Circle extends Shape ( public void drawf) ( print("Circle.draw()") ; } public void erase 0 ( print ("Circle, erase ()"l ; } ) m/ / : polymorphism/shape./Square . java package polymorphism.shape; import static net.mindview.util.Print . * ; public class Square extends Shape { public void drawO ( print ("Square, drawf)") j J

public void erase I)

printt"Square.erase(I M} / }

) ///'./(: polymorphism/shape/Triangle.java package polymorphism.shape; import static net .mmdview.util.Print * ; public class Triangle extends Shape ( public void drawO ( print ("Triangle. draw U ; ) public void erase () { print ("Triangle - erase ()"'); ) } m-.//: polymorphism/shape/RandomShapeGenerator.j ava // Una "fbrica" que genera formas aleatoriamente, package polymorphism.shape; lmport 3 ava,ut il.*; public class RandomShapeGenerator ( private Random rand new Random(47); public Shape next() ( switch(rand.nextInt( 3)) { default: case 0: return new Circle(); case 1: return new Square() ; case 2: return new Triangle();

I i ///<//: polymorphism/Shapes.java // Polimorfismo en Java, import pclymorphi sm.shape.; public class Shapes ( private static RandomShapeGenerator gen new RandomShapeGenerator0; public static void main(String[] args) { Shaped s * new Shape f 9]; // Rellena la matriz con formas: for lint i = 0 ; i <r s.length; i*+) s(i) gen.next(); // Realiza llamadas a mtodos polimrficos: for(Shape shp : s) shp.draw();

) ) /* Output: Triangle.draw (J Trtangle.draw {) Square.draw() Triangle.draw() Square.draw() Triangle. drawO Square.draw() Triangle.draw() Circle.draw I)

///:

La clase base Shape establece la interfaz comn para cualquier otra clase que herede de Shape; en trminos conceptuales, representa a todas las formas que puedan dibujarse y borrarse. Cada clase derivada sustituye estas definiciones con el fin de proporcionar un comportamiento distintivo para cada tipo especfico de forma.

RandomMiapecnerator es una especie de fbrica'* que genera una referencia a un objeto Shape aleatoriamente seleccionado cada vez que se invoca su mtodo next( ). Observe que el upcasting se produce en las instrucciones return. cada una de las cuales toma una referencia a C'ircle. Square o Triangle y la devuelve desde ne\t() con el tipo de retomo. Shape. Por tanto, cada vez que se invoca ne\t( ), nunca tenemos la oprotumdad de ver de que tipo especifico se trata, ya qje siempre obtenemos una referencia genrica a Shape

main) contiene una matriz de referencias Shape que se rellena mediante llamadas a RandomShapefcnerator.ne\t( ). En este punto, sabemos que tenemos objetos Shape. pero no podemos ser ms especficos (ni tampoco puede serlo el compilador). Sin embargo, cuando recorremos esta matriz e invocamos draw( ) para cada objeto, tiene lugar el comportamiento correspondiente a cada tipo especfico, como por arte de magia, tal y como puede ver si analiza la salida que se obtiene al ejecutar el programa.

La razn de crear las formas aleatoriamente es que asi puede percibirse mejor que el compilador no puede tener ningn conocimiento especial que le permite hacer las llamadas correctas en tiempo de compilacin. Todas las llamadas a dra\\() deben tener lugar mediante el mecanismo de acoplamiento dinmico

Ejercicio 2: (I) Aada la anotacin ( Override al ejemplo de procesamiento de formas.

Ejercicio 3: (I) Aada un nuevo mtodo a la clase base de Shapes.java que imprima un mensaje, pero sin sustituirlo

en las clases derivadas, Explique lo que sucede. Ahora, sustituyalo en una de las clases derivadas pero no en las otras y vea lo que sucede. Finalmente, sustituyalo en todas las clases derivadas.

Ejercicio 4: (2) Aada un nuevo tipo de objeto Shape a Shapes.java y verifique en main ) que el polimorfismo fun

ciona para el nuevo tipo al igual que para los tipos anteriores.

Ejercicio 5: (l) Partiendo del Ejercicio l, aada un mtodo wheels() a Cycle, que devuelva el nmero de ruedas.

Modifique ride( ) para invocar wheels() y ven fique que funciona el polimorfismo. Ampliabilidad

Volvamos ahora al ejemplo de los instrumentos musicales. Debido al polimorfismo, podemos aadir al sistema todos los nuevos tipos que deseemos sin modificar el mtodo iune( ). fcn un programa orientado a objetos bien diseado, la mayora de los mtodos lo todos ellos) seguirn el mtodo de tune( ) y slo se comunicarn con la interfaz de la clase base. Ese tipo

de programas es extensible aadir nueva funcionalidad datos a partir de la clase base manipulan la interfaz de la modificados para poder utilizar

[ampliable) porque puede heredando nuevos tipos de comn Los mtodos que clase base no necesitaran ser las nuevas clases.

Considere lo que sucede si tomamos el ejemplo de los instrumentos v aadimos ms mtodos a la clase base y una serie de clases nuevas. Puede ver el diagrama correspondiente al llnal de la pgina anterior.

Todas estas nuevas clases funcionan correctamente con el mtodo antiguo Uine( ). sin necesidad de modificarlo. Incluso si tune() se encontrara en un archivo separado y aadiramos nuevos mtodos a la interfaz de Instrument. tunc( ) seguira funcionando correctamente, sin necesidad de recompilarlo. He aqu la implemcntacin del diagrama: //: polymarphism/mu8c3/Music3 .java

// Un programa ampliable. package polymorphism.music3; import polymorphism.music.Note; import static net .mindview.util. Print. ; class Instrument ( voidplaytNote n) { print("Instrument.play() String what 0 ( return "Instrument*; ) void adjust{) ( print("Adjusting Instrument"); | l class Wind extends Instrument { void play(Note n) f print("Wind.play() " n)j ) String what 0 ( return "Wind"; ) void adjustO ( print("Adjusting Wind"); ) H n); )

) class Percussion extends Instrument ( voidplaylNote n) ( print I "Percussion.play U String what() ( return "Percussion"; ) void adjust () ( print "Adjusting Percussion'*); ) " * n); )

} class Stringed extends Instrument ( voidplayiNote n) \ {printfStringed.play() * 4 n);

String what() ( return "Stringed"; ) voidadjust() ( print I"Adjusting Stringed"); )

} class Brass extends Wind j voidplayiNote n) ( print<MBrass.play\) " + n); } voidadjust()| print("Adjusting Brass"/ }

) class Woodwind extends Wind ( voidplay(Note n) } (print("Woodwind.play{) " T n);

String whatO ( return "Woodwind"; )

) public class Music3 { // No importa el tipo, por lo que los nuevc3 // tipos aadidos al sistema funcionan bien: public static void tune(Instrument i) { u ...
i.

play Note.MIDDLE C);

i 8 Polimorfismo 483 public static void tuneAll(Instrument[J e) ( for(Instrument i : e)tune(i); public static void mainStringU args) ( f/ pcasting durante la adicin a la matriz: Instrument l] orchestra = ( new Wind{), new Percussiont) , new StringedO, new Brass(). new WoodwindO t uneAl 1 (orchest ra! ,*

} ) / Output: Wind.play0 MIDDLE_C Percussion.playO MIDDLE_C Stringed. playO MIDDLE_C Brass.play(J MIDDLE_C woodwind. play () MIDDLE_C ///:-

Los nuevos mtodos son what( ). que devuelve una referencia String con una descripcin de la clase y adjust( ), que proporciona alguna forma de ajustar cada instrumento.

bn main( ). cuando insertamos algo dentro de la matriz orchestra. se produce automticamente una generalizacin a Instrument.

Podemos ver que el mtodo tune< ) es completamente ignorante de todos los cambios de cdigo que han tenido lugar alrededor suyo, a pesar de lo cual sigue funcionando perfectamente. sta es. exactamente. la funcionalidad que se supone que el polimorfismo debe proporcionar. Los cambios en el cdigo no generan

i 8 Polimorfismo 484 ningn problema en aquellas partes del programa que no deban verse afectadas. Dicho de otra forma, el polimorfismo es una tcnica importante con la que el programador puede "separar las cosas que cambian de las cosas que permanecen.

Ejercicio 6: raiz

(1)Modifique Music3.java de modo que whaf() se convierta en el mtodo toString() del objeto

Object. Pruebe a imprimir los objetos Instrument utilizando System.out.println( ) (sin efectuar ninguna proyeccin de tipo).

Ejercicio 7: funciona

(2)Aadaunnuevo

tipo de objeto Instrument a Music3.java y verifique que el polimorfismo

para el nuevo tipo.

Ejercicio 8:

(2)Modifique!Muslc3.javapara que

genere aleatoriamente

objetos Instrument de la

i 8 Polimorfismo 485 misma forma que lo

hace Shapes.java.

Ejercicio 9: (3K ree una jerarquiaa de herencia Rodent .Mouse. Gerbil. Hmster, etc (roedor ratn, jerbe. hmster.

etc.). En la clase base proporcione los mtodos que son comunes para todos los roedores, y sustituya estos mtodos en las clases derivadas para obtener diferentes comportamientos dependiendo del tipo especifico de roedor. Cree una matriz de objetos Rodent. rellnela con diferentes tipos especficos de roedores e invoque los mtodos de la clase base para ver lo que sucede.

Ejercicio 10: (3) Cree una clase base con dos mtodos. Ln el primer mtodo, invoque el segundo mtodo. Defina una

i 8 Polimorfismo 486 clase que herede de la anterior y sustituya el segundo mtodo. Cree un objeto de la clase derivada, realice una generalizacin (u/Kasting) al tipo base y llame al primer mtodo. Explique lo que sucede. Error: sustitucin de mtodos private

He aqu un ejemplo de error de un programa que se puede cometer de manera inadvertida; //: polymorphism/Pn vateOverride.java // Intento de sustituir un mtodo privado. package polymorphism; import static net.mindview.util.Print.*; public class PrivateOverride ( prvate void f() ( print("prvate fO"); ) public static void main IString [] args) ( PrivateOverride po = new Derived O; po.fO;

) class Derived extends PrivateOverride { public void () { print^"public } } /+ Output: private f()

i 8 Polimorfismo 487 ///:-

Podra esperar, razonablemente, que la salida fuera public f( ). pero los mtodos privados son automticamente de tipo final, y estn tambin ocultos a ojos de la clase derivada. Por esta razn, el mtodo f() de la clase derivada es, en este caso, un mtodo completamente nuevo, ni siquiera est sobrecargado, ya que la versin de f( ) en la clase base no es visible en Derived.

El resultado de esto es que slo los mtodos no privados pueden ser sustituidos, as que hay que estar atento al intento incorrecto de sustituir mtodos de tipo prvate, ya que esos intentos no generan ninguna advertencia del compilador, sino que el sistema no har, seguramente, lo que se espera. Para evitar las confusiones, conviene utilizar en la clase derivada un nombre diferente al del mtodo private de la clase base. Error: campos y mtodos static

Una vez familiarizados con el tema del polimorfismo, podemos tender a pensar que todo ocurre polmdicamente. Sin embargo, las nicas llamadas que pueden ser polimorficas son las llamadas a mtodos normales. Por ejemplo, si accedemos a un campo directamente, ese acceso se resolver en tiempo de compilacin, como se ilustra en el siguiente ejemplo:18 //: polymorphism/FieldAccess.java // El acceso directo a un campo se determina en tiempo de compilacin. class Super { public int field * 0; public int getFieldO ( retum field; }

18

Gracia* a Ratuiy NichoU por planteat esla cuestin.

i 8 Polimorfismo 488 ) class Sub extends Super { public int field = l; public int getFieldO ( retum field; ) public int getSuperField() ( retum super.field; }

) public class FieldAccess { public static void main(Stringfj args) { Super sup new Sub () ; // Upcast System.out.println("sup.field * + sup.field + ", sup.getField() sup.getField()); Sub sub new Sub () ; System.out .println ("sub. field = " - sub.field ", sub.getField() = " + sub.getFieldO ** ", sub.getSuperFieldO * * sub.getSuperField 01;

> | / OUtpfUC: sup-field =* Q, sup.aetField( ) * 1 sub.field * 1, sub.aetFieldi) = i, sub.getSuperField() = 0 ///:-

Cuando un objeto Sub se generaliza a una referencia Super. los accesos a los campos son resueltos por el compilador, por

i 8 Polimorfismo 489 lo que no son polimrficos. En este ejemplo, hay asignado un espacio de almacenamiento distinto para Super.field y Sub.field Por tanto. Sub contiene realmente dos campos denominados field: el suyo propio y el que obtiene a partir de Super. Sin embargo, cuando se hace referencia al campo field de Super no se genera de forma predeterminada una referencia a la versin almacenada en Super; para poder acceder al campo field de Super es necesario escribir explcitamente super.field.

Aunque esto ltimo pueda parecer algo confuso, en la prctica no llega a plantearse casi nunca, por una razn: por regla general, se definen todos los campos como prvate, por lo que no se accede a ellos directamente, sino slo como efecto secundario de la invocacin a mtodos. Adems, probablemente nunca le demos el mismo nombre de la clase buse a un campo de la clase derivada, ya que eso resultara muy confuso.

Si un mtodo es de tipo static. no se compona de forma poliinrfca //: polymorphism/StaticPolymorphism. java Los mtodos estticos no son polimrficos. class StaticSuper ( public static String staticGetO ( return Base staticGetOHM;

) public String dynamicGet0 { return "Base dynamicGet 0M ;

i 8 Polimorfismo 490 )

} class StaticSub extends StaticSuper { public static String staticGetO { return "Derived staticGetO";

) public String dynamicGet() ( return "Derived dynamicGet (),*

} i public class StaticPolymorphism { public static void main (String [] args) ( StaticSuper sup = new StaticSubO; // Generalizacin System.out .println(sup.staticGet ()); System.out .println(sup.dynamicGet {} ) ;

} ( / * Output:

i 8 Polimorfismo 491 Base staticGetO Derived dynamicGet()

///:-

Los mtodos estticos estn asociados con la clase y no con los objetos individuales. Constructores y polimorfismo

Como suele suceder, los constructores difieren de los otros tipos de mtodos, tambin en lo que respecta al polimorfismo. Aunque los constructores no son polimrficos (se trata realmente de mtodos estticos, pero la declaracin static es implcita). tiene gran importancia comprender cul es la forma en que funcionan los constructores dentro de las jerarquas complejas y en presencia de polimorfismo. Esta compresin de los fundamentos nos ayudar a evitar errores desagradables Orden de las llamadas a los constructores

Hemos hablado brevemente del orden de las llamadas a Jos constructores en el Capitulo 5. niaalbadn v limpieza, y tambin el Captulo 7. Reutilizacin Je clases, pero eso fue antes de introducir el concepto de polimorfismo.

i 8 Polimorfismo 492 El constructor de la clase base siempre se invoca durante el proceso de construccin correspondiente a una clase derivada. Esta llamada provoca un desplazamiento automtico hacia arriba en la jerarqua de herencia, invocndose un constructor para todas las clases base. Esto tiene bastante sentido, porque el constructor tiene asignada una tarea especial: garantizar que el objeto se construye apropiadamente Una clase derivada slo tiene acceso a sus propios miembros y no a los de la clase base (aquellos miembros tpicamente de tipo prvate). Slo el constructor de la clase base dispone del conocimiento y del acceso adecuados para inicializar sus propios elementos. Por tanto, resulta esencial que se invoquen todos los constructores, en caso contrario, no podra construirse el mtodo completo. Esta es la razn por la que el compilador impone que se realice una llamada al constructor para cada pane de una clase deriv ada. Si no especificamos explcitamente una llamada a un constructor de la clase base dentro del cuerpo de la clase derivada, el compilador invocar de manera automtica el constructor predeterminado. Si no hay ningn constructor predeterminado, el compilador generar un error (en aquellos casos en que una determinada clase no tenga ningn constructor, el compilador sintetizar automticamente un constructor predeterminado).

Veamos un ejemplo que muestra los efectos de la composicin, de la herencia y del polimorfismo sobre el orden de construccin: //: polymorphism/Sandwich.java / / Orden de las llamadas a los constructores. package polymcrphism; import static net.mindview.til.Print.; class Meal ( Meal O { print {"Meal O MJ ; ) class Bread ( Bread!) ( print ("Eread() w) ,* )

) class Cheese ( Cheese0 ( print < "Cheese O") ; }

i 8 Polimorfismo 493 ) class Lettuce ( LettuceO [ print tLettuce O nl ; ) i class Lunch extends Meal ( Lunch() [ print!"Lunch0 ") ; )

} class PortableLunch extends Lunch ( PortableLunch0 ( print"PortableLunchO");)

) public class Sandwich extends PortableLunch { private Bread b = new Breado prvate Cheese c = new Cheese t); private Lettuce 1 = new LettuceO ; public Sandwich{) { print("Sandwich O H > ; } public static void mam(Stringf] args) { new Sandwich < ) ) ) /* Output: Meal( )

Lunch () P o r tableLunchO BreadO Cheeae() LettuceO Sandwich(i *///:-

i 8 Polimorfismo 494 Este ejemplo crea una clase compleja a partir de otras clases y cada una de estas clases dispone de un constructor que se anuncia a s mismo. La clase importante es Sandwich, que refleja tres niveles de herencia (cuatro si contamos la herencia implcita a partir de Object) y tres objetos miembro. Podemos ver en main( ) la salida cuando se crea un objeto Sandwich Esto quiere decir que el orden de llamada a los constructores para un objeto complejo es el siguiente:

1.

Se invoca al constructor de la clase base Este paso se repite de forma recursiva de modo que la raz de la jerarqua se construye en primer lugar, seguida de la siguiente clase derivada, etc., hasta alcanzar la clase situada en el nivel ms profundo de la jerarqua.

2.

Los inicializadores de los miembros se invocan segn el orden de declaracin.

3.

Se invoca el cuerpo del constructor de la clase derivada.

El orden de las llamadas a los constructores es importante. Cuando utilizamos los mecanismos de herencia, sabemos todo acerca de la clase base y podemos acceder a los miembros de tipo puhlic y protected de la misma. Esto quiere decir que debemos poder asumir que todos los dems miembros de la clase base son vlidos cuando los encontremos en la clase derivada. En un mtodo normal, el proceso de construccin ya ha tenido lugar, de modo que todos los miembros de todas las partes del objeto habrn sido construidos. Sin embargo, dentro del constructor debemos poder estar seguros de que todos los miembros que utilicemos hayan sido construidos. La umea forma de garantizar esto es invocando primero al constructor de la clase base. Entonces, cuando nos encontremos dentro del constructor de la clase derivada, todos los miembros de la clase base a ios que queremos acceder ya habrn sido inicializados. Saber que todos los miembros son vlidos dentro del constructor es tambin la razn de que. siempre que sea posible, se deban micializar todos los

i 8 Polimorfismo 495 objetos miembro (los objetos incluidos en la clase mediante los mecanismos de composicin) en su punto de definicin dentro de la clase (por ejemplo,

c y I en el ejemplo anterior). Si se ajusta a esta practica a la hora de programar, le ser ms fcil garantizar que todos los miembros de la clase base y objetos miembro del objeto actual hayan sido inicial izados. I amentablemenie, este sistema no nos permite gestionar todos los casos, como veremos en la siguiente seccin.
b.

Ejercicio 11: (l) Aada una clase Pickle a Sandwch.java. Herencia y limpieza

Cuando se utilizan los mecanismos de composicin y de herencia para crear una nueva clase, la mayor parte de las veces no tenemos que preocupamos por las tareas de limpieza; los subobjetos pueden normalmente dejarse para que los procese eJ depurador de memoria. Sin embargo, si hay algn problema relativo a la limpieza, es necesario actuar con diligencia y crear un mtodo dispnse( ) (ste es el nombre que yo he seleccionado, pero usted puede utilizar cualquier otro que indique que estamos deshacindonos del objeto) en la nueva clase. Y, con la herencia, es necesario sustituir disposc< ) en la clase derivada si necesitamos realizar alguna tarea de limpieza especial que tenga que tener lugar como piule de la depuracin de memoria. Cuando se sustituya dispose( ) en una clase heredada, es importante acordarse de invocar la versin de dispose( ) de la clase base, ya que en caso contrario las tareas de limpieza propias de la clase base no se llevarn a cabo. El siguiente ejemplo ilustra esta situacin: //: polymorphi sm/Frog.j ava // Limpieza y herencia, package polymorphism; import static net.mindview.util.Print. * ; class Characteristic ( prvate String s?

496 Piensa en Java Characteristic(String si | thia.s = s;print(Creating Characteristic * * s) ; protected void dispose(> { print<"disoosina Characteristic * s ) ; }

) class Description { private String s; Description(String s) { this.s s; print ("Creating Description " + s) ;

] protected void dispose() ( print ('disposing Description " * s); I

} class LivingCreature ( private Characteristic p = new Characteristic("is alive"); private Description t =

8 Polimorfismo 497 new Description!"Basic LivingCreature() ( print("LivingCreature()"); Living Creature");

) protected void dispose() ( print("LivingCreature dispose" ) ; t.dispose(}; p.dispose 0 :

> class Animal extends LivingCreature ( private Characteristic p = new Characteristic("has heart"); private Description t * new Description<"Animal not VegetableH); Animal() { print 1"Animal()"); ) protected void dispose() ( print["Animal dispose) ; t.dispose(); p.dispose(); super.dispose() ;

498 Piensa en Java i

) class Amphibian extends Animal { private Characteristic p = new characteristic("can Description t = print("Amphibian{)"); live in water"); private

new Description!"Both water and land"); Amphibian() {

} protected void dispose() { print("Amphibian dispose") ; t.dispose() ;p.dispose ) r super.di spose();

8 Polimorfismo 499 ) public class Frcg extends Amphibian ( private Characteristic p * new Characteristic("Croaks"I; private Description t = new Description("Eats Bugs*'J; public Frog() ( print("Frog 0 *>; } protected void dispose() ( print I"Frog dispose"); t.dispose(); p.dispose 0 ; super.dispose(i; i public static void mainiString[] args? { Frog frog * new FrogO; print ("Bye! " ) ; frog.dispose();

) ) /* Output: Creating Characteristic is alive Creating Description Basic Living Creature LvingCreatureI) Creating Characteristic has heart Creating Description Animal not Vegetable Animal() Creating Characteristic can live in water Creating Description Both water and land Amphibian{) Creating Characteristi c Croaks Creating Description

500 Piensa en Java Eats Frog( ) Bye! Frog dispose disposing Description Eats Bugs disposing Characteristi c Croaks Amphibian dispose disposing Description Both water and land disposing Characteristic can live in water Animal dispose disposing Description Animal not Vegetable disposing characteristic has heart LivmgCreature dispose disposing Description Basic Living Creature disposing Characteristic is alive Bugs

*///:-

Cada clase de la jerarqua tambin contiene objetos miembro de los tipos Characteristic y

8 Polimorfismo 501 Description, que tambin habr que borrar. Fl orden de borrado debe ser el inverso del orden de inicializacin. por si acaso uno de los subobjetos depende del otro. Para los campos, esto quiere decir el inverso del orden de declaracin (puesto que los campos se inicializan en el orden de declaracin) Para las clases base (siguiendo la norma utilizada en C++ para los destructores), debemos realizar primero las tareas de limpieza de la clase derivada y luego las de la clase base. La razn es que esas tareas de limpieza de la clase derivada tuvieran que invocar algunos mtodos de la clase base que requieran que los componentes de la clase base continen siendo accesibles, asi que no debemos destruir esos componentes prematuramente. Analizando la salida podemos ver que se borran todas las partes del objeto Frog en orden inverso al de creacin.

A partir de este ejemplo, podemos ver que aunque no siempre es necesario realizar tareas de limpieza, cuando se llevan a cabo es preciso hacerlo con un gran cuidado y una gran atencin

Ejercicio 12: (3) Modifique e) Ejercicio 9 para que se muestre el orden de inicializaeiu de las clases base y de las cla

ses derivadas. Ahora aada objetos miembro a las clases base y derivadas, y muestre el orden en que se lleva a cabo la micializacion durante el proceso de construccin.

502 Piensa en Java Observ e tambin en el ejemplo anterior que un objeto Frog posee sus objetos miembro: crea esos objetos miembro y sabe durante cunto tiempo tienen que existir (tanto como dure el objeto Frog). de modo que sabe cundo invocar el mtodo disposi ) para borrar los objetos miembro. Sin embargo, si uno de estos objetos miembro es compartido con otros objetos, el problema se vuelve ms complejo y no podemos simplemente asumir que basta con invocar dispose(). En estos casos, puede ser necesario un recuento de referencias para llevar la cuenta del nmero de objetos que siguen pudiendo acceder a un objeto compartido. He aqu un ejemplo: //: polymorphism/ReferenceCountlng. java // Limpieza de objetos miembro compartidos, import static net.mindview.util.Print.*j class Shared ( private int refcount * 0 ; private static long counter = 0 ; private final long id * counter 4 -+; public Shared(I { print("Creating " + this I;

) public void addRefd f ref count ; ) protected void dispose(I { if(--refcount == 0 ) print(Disposing M + this);

} public String toStringO ( return "Shared H + id; )

8 Polimorfismo 503 ) class Composing { private Shared shared; private static long counter = 0 ; private final long id counter+-r; public Composing(Shared shared) ( print ("Creating M this)? this.shared = shared; this. shared. addRef {) ,*

! protected void dispose 0 ( print"d isposing + this)/ shared.dis DOse();

) public String toStrina() ( return "Composing " * id; } J public class ReferenceCounting ( public static void main(String[] arqs) ( Shared shared = new Shared()/ Composing[J composing = ( new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared), new

504 Piensa en Java Composing(shared) for(Composing composing) c.dispose(); c ); :

) ) / Output: Creating Shared 0 Creating Composing 0 Creating Composing 1 Creating Campos ing 2 Creati ng Compos ing 3 Creati ng Compos ing 4 dispos ing Compos ing 0 dispos ing Compos ing 1 di sposin g Compos ing 2 ispos ing Compos ing 3 dispos ing Compos ing 4 Dispcs ina Shared

8 Polimorfismo 505 0 ///i

El contador stalic long counter lleva la cuenta del numero de instancias de Shared que son creadas y tambin crea un valor para id El tipo de counter es long en lugar de int. para evitar el desbordamiento (se trata slo de una buena prctica de programacin: es bastante improbable que esos desbordamientos de contadores puedan producirse en ninguno de los ejemplos de este libro) La variable id es de tipo final porque no esperamos que cambie de valor durante el tiempo de vida del objeto.

Cuando se asocia el objeto compartido a la clase, hay que acordarse de invocar addRef' ). pero el mtodo dispose( ) llevar la cuenta del nmero de referencias y decidir cundo hay que proceder con las tareas de limpieza. Esta tcnica requiere un cierta diligencia por nuestra parte, pero si estamos compartiendo objetos que necesiten que se lleve a cabo una determinada tarea de limpieza, no son muchas las opciones que tenemos.

Ejercicio 13: (3) Aada un mtodo rmalize( ) a ReferenceCounting.java para verificar la condicin de terminacin (vase el Capitulo 5, Inicial ilacin y limpieza).

506 Piensa en Java Ejercicio 14: (4) Modifique el Ejercicio 12 para que uno de los objetos miembro sea un objeto compartido. Utilice el

mtodo de recuento del nmero de referencias y demuestre que funciona adecuadamente. Comportamiento constructores de los mtodos polimrficos dentro de los

La jerarqua de llamada a constructores plantea un dilema interesante. Qu sucede si estamos dentro de un constructor e invocamos un mtodo con acoplamiento dinmico del objeto que est siendo construido?

Dentro de un mtodo normal, la llamada con acoplamiento dinmico se resuelve en tiempo de ejecucin, porque el objeto no puede saber si pertenece a la clase en la que se encuentra el mtodo o a alguna de las clases derivadas de la misma.

Si invocamos un mtodo con acoplamiento dinmico dentro de un constructor, tambin se utiliza la definicin sustituida de dicho mtodo (es decir, la definicin del mtodo que

8 Polimorfismo 507 se encuentra en la clase actual). Sin embargo, el efecto de esta llamada puede ser inesperado, porque el mtodo sustituido ser invocado antes de que el objeto haya sido completamente construido. Esto puede hacer que queden ocultos algunos errores realmente difciles de detectar.

Conceptual mente, la tarea del constructor es hacer que el objeto comience a existir (lo que no es una tarea trivial). Dentro de cualquier constructor, puede que el objeto completo slo est formado parcialmente, ya que de lo nico que podemos estar seguros es de que los objetos de la clase base han sido nicializados Si el constructor es slo uno de los pasos a la hora de construir un objeto de una clase que haya sido derivada de la clase correspondiente a dicho constructor, las partes derivadas no habrn sido todava inicializadas en el momento en que se invoque al constructor actual. Sin embargo, una llamada a un mtodo con acoplamiento dinmico se adentra en la jerarqua de herencia, invocando un mtodo dentro de una clase derivada. Si hacemos esto dentro de un constructor, podramos estar invocando un mtodo que manipulara miembros que todava no han sido micializados. lo cual constituye una receta segura para que se produzca un desastre

Podemos ver el problema en el siguiente ejemplo: //: polymorphism/PolyConstructo rs.java // Los constructores en presencia de polimorfismo // pueden no producir los resultados esperados, imporc static net.mindview.til.Print.*; class Glyph ( void draw() | prmt ("Glyph.draw()n); } GlyphO ( prrnt ("Glyph () before draw 1") ;

508 Piensa en Java draw(); print("Glyphi) after draw(>">; i

} class RoundGlyph extends Glyph { prvate int radlus 1 ; RoundGlyph(Int r) ( radius * r; print ("RoundGlyph. radius); ) void draw<) ( print("RoundGlyph.drawO, radius * H + radius); RoundGlyph O . radius = ** +

) public class PolyConstructors (

8 Polimorfismo 509 public static void main(Stringti args) { new RoundGlyph(5 >; J } /* Output: Glyph() before drawi) RoundGlyph.dra w()f radius = 0 GlyphO after draw() RoundGlyph.RoundG lyph<), radius = 5 *///=-

Glyph.dra>v( ) est diseado para ser sustituido, lo que se produce en RoundGlyph Pero el constructor de Glyph invoca este mtodo y la llamada termina en RoundGlyph.dra\v( ). que parece que fuera la intencin original. Pero si examinamos la salida, podemos ver que cuando el constructor de Glvph invoca dravv( ). el valor de radius no es ni siquiera el valor inicial predeterminado de 1. sino que es 0. Esto provocar, probablemente, que se dibuje en la pantalla un punto, o nada en absoluto, con lo que el programador se quedar contemplndolo tratando de imaginar por qu no funciona el programa.

El orden de inicializacin descrito en la seccin anterior no est completo del todo, y ahi es donde radica lu clave para resolver el misterio. El proceso real de inicializacin es:

510 Piensa en Java El almacenamiento asignado al objeto se inicializa con ceros binarios antes de que suceda ninguna otra cosa.
1.

Los constructores de las clases base se invocan tal y como hemos descrito anteriormente. En este punto se invoca el mtodo sustituido draw( ) (si, se invoca antes de que llame al constructor de RoundGlyph) y ste descubre que el valor de radius es cero, debido al Paso 1.
2.

3.

Los inicializadores de los miembros se invocan segn el orden de declaracin.

4.

Se invoca el cuerpo del constructor de la clase derivada.

La parte buena de todo esto es que todo se inicializa al menos con cero (o con lo que cero signifique para ese tipo de datos concreto) y no simplemente con datos aleatorios. Esto incluye las referencias a objetos que han sido incluidas en una clase a travs del mecanismo de composicin, que tendrn el valor nuil Por tanto, si nos olvidamos de inicializar esa referencia, se generar una excepcin en tiempo de ejecucin. Todo lo dems lomar el valor cero, lo que usualmente nos sirve como pista a la hora de examinar la salida.

8 Polimorfismo 511 Por otro lado, es posible que el programador se quede horrorizado al ver la salida de este programa: hemos hecho algo perfectamente lgico, a pesar de lo cual el comportamiento es misteriosamente errneo, sin que el compilador se haya quejado (C+-* produce un comportamiento ms racional en esta situacin). Los errores de este tipo podran quedar ocultos fcilmente, necesitndose una gran cantidad de tiempo para descubrirlos.

Como resultado, una buena directriz a la hora de implementar los constructores es: "Haz lo menos posible para garantizar que el objeto se encuentre en un estado correcto y, siempre que puedas evitarlo, no invoques ningn otro mtodo de esta clase". Los nicos mtodos seguros que se pueden invocar dentro de un constructor son aquellos de tipo final en la clase base (esto tambin se aplica a los mtodos privados, que son automticamente de tipo final). Estos mtodos no pueden ser sustituidos y no pueden, por tanto, damos este Upo de sorpresas. Puede que no siempre seamos capaces de seguir esta direc- jn2. pero al menos debemos tratar de cumplirla.

Ejercicio 15: (2) Aada una clase KectanuiilarGIyph a PolvConstructors.java e ilustre el problema descrito en esta

seccin.

512 Piensa en Java Tipos de retorno covariantes

Java SE5 aade los denominados tipos de retorno covariantes, lo que quiere decir que un mtodo sustituido en una clase derivada puede devolver un tipo derivado del tipo devuelto por el mtodo de la clase base: //: polymorphism/Covariant Retum .java clas Grain { public String toStringU { retum "Grain"; ) j class Wheat extends Grain ( public String toStringU ( retum "Wheat"; }

) class Mili { Grain process() { retum new GrainO; ] l class WneatMill extends Mili { Wheat orocess) j retum new Wheat (); }

) public class CovanantRetum { public static void main(StringIJ args) {

8 Polimorfismo 513 Mili m - new Mili(); Grain g = m.processi ) ; System .out.p rintln (g); m = new WheatM ill{); g m.proc ess{ ) SyGtem.out .println(g);

) ] / Output! Grain Wheat

*///:-

La diferencia clave entre Java SE5 y las versiones anteriores es que en stas se obligara a que la versin sustituida de proct*ss( ) devolviera Grain, en lugar de Wheat, a pesar de que Wheat deriva de Grain y signe siendo, por tanto, un tipo de retomo legtimo. Los tipos de retorno covariantes permiten utilizar el tipo de retomo Wheat ms especifico.

514 Piensa en Java Diseo de sistemas con herencia

Una vez que sabemos un poco sobre el polimorfismo, puede llegar a parecemos que todo debera heredarse, ya que el polimorfismo es una herramienta tan inteligente. Pero la realidad es que esto puede complicar nuestros diseos innecesariamente, de hecho, si decidimos utilizar la herencia como primera opcin a la hora de utilizar una clase existente con el fin de formar otra nueva. las cosas pueden volverse innecesariamente complicadas.

Una tcnica mejor consiste en tratar de utilizar primero la composicin, especialmente cuando no resulte obvio cul de los dos mecanismos debera emplearse. La composicin no hace que el diseo tenga que adoptar una jerarqua de herencia. Pero, asimismo, la composicin es ms flexible, porque permite seleccionar dinmicamente un tipo (y por tanto un compor- (amiento), mientras que la herencia exige que se conozca un tipo exacto en tiempo de compilacin. El siguiente ejemplo ilustra esto: //: polymorphism/Transmogrify.java // Modificacin dinmica del comportamiento de un objeto // mediante la composicin (el patrn de diseo basado en estados]. import static net.mindview.til.Print; class Actor ( public void actO ()

8 Polimorfismo 515 class HappyActor extends Actor { public void act<> { print("HapoyActor"); }

} class SadActor extends Actor ( public void act(l ( print <"SadActor") ,* )

i class Stage { prvate Actor actor = r.ew HappyActor () public void changeO ( actor = new SadActor(J; ) public void performPlayO { actor.actO; }

) public class Transmogrify { public static void main(String(l args) { Stage stage = new Stage O; stage.perf ormPlay( ) ;

516 Piensa en Java stage.chan ge(); stage.perf ormPlay();

} } / Output: HappyActor SadActor

V//:-

Un objeto Stage contiene una referencia a un objeto Actor, que se inicializa para que apunte a un objeto HappyActor. Esto significa que performP!ay( ) produce un comportamiento concreto. Pero, como una referencia puede redingirse a un objeto distinto en tiempo de ejecucin, podramos almacenar una referencia a un objeto SadActor en actor, y entonces el comportamiento producido por performPlav ) variara. Por tanto, obtenemos una mayor flexibilidad dinmica en tiempo de ejecucin (esto se denomina tambin patrn ci diseo basado en estados, consulte Thinking in Patterns (w'tth Java) en wwu. Mindtfew.net). Por contraste, no podemos decidir realizar la herencia de forma diferente en tiempo de ejecucin, el mecanismo de herencia debe estar perfectamente determinado en tiempo de compilacin.

8 Polimorfismo 517 Una regla general sera: Utilice la herencia para expresar las diferencias en comportamiento y los campos para expresar las variaciones en el estado". En el ejemplo anterior se utilizan ambos mecanismos: definimos mediante herencia dos clases distintas para expresar la diferencia en el mtodo act( ) y Stage utiliza la composicin para permitir que su estado sea modificado. Dicho cambio de estado, en este caso, produce un cambio de comportamiento.

Ejercicio 16: (3) Siguiendo el ejemplo de Traiismoirify.java, cree una clase Starship que contenga una referencia

AlertStatus que pueda indicar tres estados distintos. Incluya mtodos para verificar los estados. Sustitucin y extensin Podra parecer que la forma ms limpia de crear una jerarqua de herencia seria adoptar un enfoque "puro; es decir, slo los mtodos que hayan sido establecidos en la clase base sern sustituidos en la clase derivada, como puede verse en este diagrama

:Shape

draw() erase ()Triang le draw() erase{ )

Circle draw() erase()

Square draw() erase()

Esio

podra decirse que es una relacin de tipo *es-un porque la interfaz de una clase establece lo que dicha clase es La herencia garantiza que cualquier clase derivada tendr la interfaz de la clase base y nada ms. Si seguimos este diagrama, las clases derivadas no tendrn nuda ms que lo que la interfaz de la clase base ofrezca.

Esto podra considera

rse como una sustituci n pura, porque podemos sustituir perfecta mente un objeto de la clase base o un objeto de una clase derivada y no nos hace falta conocer ninguna informaci n adicional acerca de las subclases a la hora de utilizarlas : Habla con Shape Circle Square. Line o un nuevo tipo de Shape Mensaj e R el a ci n e su n "

En otras palabras, la clase base puede recibir cualquier mensaje que enviemos a la clase derivada, porque las des tienen exactame nte la misma interfaz. Debido a esto lo que tenemos que hacer es generaliz ar a partir de la clase derivada, sin tener que preocupa mos de ver cul es el tipo exacto del objeto con el que estemos tratando. Todo se maneja mediante el polimorfi smo. Cuando vemos las cosas de esta forma,

debe parecer que las relaciones puras de tipo esun son la forma ms lgica de implemen tar las cosas, y que cualquier otro tipo de diseo resulta confuso por comparac in. Pero esta forma de pensar es un error. Ian pronto comence mos a pensar de esta forma, miraremo sa nuestro alrededor y descubrir emos que ampliar la interfaz (mediante la palabra clave extends) es la perfecta solucin

para un problema concreto. Este tipo de solucin podra denomina rse relacin de tipo "es-comoun", porque la clase derivada es como la clase base: tiene la misma interfaz elemental y tiene, adems, otras caracterst icas que requieren mtodos adicionale s para implemen tarla sUsefu l void f() void g()

Su

ponga que esto y represe nta una interfaz complej a

Es -comoun MoreU seful void f() void g() voi d u() voi d v() voi d w()

Amplia cin de la interfaz

Aunque este enfoque tambin resulta til y lgico (dependiendo de la situacin) tiene una desventaja. La parte ampliada de la interfaz en la clase derivada no est disponible en la clase base, por lo que. una vez que efectuemos una generalizacin no podremos invocar los nuevos mtodos: Mensaje Habla con el objeto Useful parte de Useful Si no estamos haciendo generalizaciones, no debe haber ningn problema, pero a menudo nos encontraremos en situaciones en las que necesitamos descubrir el tipo exacto del objeto para poder acceder a los mtodos ampliados de dicho tipo. En la siguiente seccin se explica cmo hacer esto. Especializacin e informacin de tipos en tiempo de ejecucin

Puesto que perdemos la informacin especfica del tipo mediante el proceso de generalizacin (upeast, que consiste en moverse hacia arriba por la jerarqua de herencia), tiene bastante sentido que para extraer la informacin de tipos; es decir, para volver a descender por la jerarqua de herencia, utilicemos un proceso de especializacin (downcast). Sin embargo, sabemos que una generalizacin siempre es segura, porque la clase base no puede tener una interfaz ms amplia que la clase derivada: por tanto, se garantiza que todo mensaje que enviemos a travs de la interfaz de la clase base ser aceptado. Pero con una especializacin no sabemos realmente si una determinada forma, por ejemplo, es un crculo u otra cosa: tambin podra ser un tringulo, un cuadrado o algn otro tipo de forma

Para resolver este problema, tiene que haber alguna manera de garantizar que la especializacin se efecte de forma correcta. de modo que no hagamos accidentalmente una proyeccin sobre el tipo inadecuado y luego enviemos un mensaje que el objeto no pueda aceptar. Si no podemos garantizar que la especializacin se efecte de manera correcta, nuestro programa no ser muy seguro.

F.n algunos lenguajes (como C+-*-) es necesario realizar una operacin especial para poder llevar a cabo una especializacin de tipos de forma correcta, pero en Java todas las proyecciones de tipos se comprueban. Por tanto, aunque parezca que estemos utilizando simplemente una proyeccin de tipos normal, usando parntesis, dicha proyeccin se comprueba en tiempo de ejecucin para garantizar que se trate, de hecho, del tipo que creemos que es. Si no lo es, se obtiene una excepcin ClassCastException. Este acto de comprobacin de tipos en tiempo de ejecucin se denomina informacin de tipos en tiempo de ejecucin (RTTI, runtime type Information). El siguiente ejemplo ilustra el comportamiento de RTTl: //: polymorphism/RTTI,]ava // Especializacin en informacin de tipos en tiempo de ejecucin (RTTIJ. // ThrowsException) clase seful { puble void f() () public void g() {)

) class MoreUseful extends Useful {

} public class RTTI ( public static void main(StringH args) [ Useful [] x ^ ( new Useful(), new MoreUsefulf)

>;

x10] . t i ) s xlj .gO i / / Tiempo de compilacin: mtodo no encontrado en Useful: //i xllj.ul); ! (MoreUseful)x[II .u{)j // Especial! racin/RTTI (MoreUseful)x1011 .uO; // Excepcin generada

l i ///.-

Cuino en el diagrama anterior, MoreUseful ampla la interfaz de Useful. Pero, como se trata de una clase heredada tambin puede generalizarse a Useful. Podemos ver esta generalizacin en accin durante la inicializacin de la matriz \ en main( ) Puesto que ambos objetos de la matriz son de clase Useful. podemos enviar los mtodos f( ) y g( ) a ambos, mientras que si tratamos de invocar u( | (que slo existe en MoreUseful), obtendremos un mensaje de error en tiempo de compilacin.

Si queremos acceder a la interfaz ampliada de un objeto Morel'seful. podemos tratar de efectuar una especializacin. Si se trata del tipo correcto, la operacin tendr xito. En caso contrario, obtendremos una excepcin ClassCastException No es necesario escribir ningn codigo especial para esta excepcin, ya que indica un error del programador que puede producirse en cualquier lugar del programa. La etiqueta de comentario {ThrowsException} le dice al sistema de construccin de los ejemplos de este libro que cabe esperar que este programa genere una excepcin al ejecutarse.

L! mecanismo RTT1 es ms complejo de lo que este ejemplo de proyeccin simple permite intuir. Por ejemplo, existe una forma de ver cul es el tipo con el que estamos tratando untes de efectuar la especializacin. El Captulo 14, Informacin Je tipos est dedicado al estudio de los diferentes aspectos de la informacin de tipos en tiempo de ejecucin en Java

Ejercicio 17: (2) Utilizando la jerarqua Cvcle del Ejercicio I. aada un mtodo balancc( ) a l'nicycle y Bicycle, pero

no a Tricycle. Cree instancias de los tres tipos y generalcelas para formar una matriz de objetos Cycle Trate de invocar balance( ) en cada elemento de la matriz y observe los resultados. Realice una especia- lizacin e invoque balance( ) y observe lo que sucede. Resumen

Polimorfismo significa diferentes formas. En la programacin orientada a objetos, tenemos una misma interfaz definida en la clase base y diferentes formas que utilizan dicha interfaz: las diferentes versiones de los mtodos dinmicamente acopiados.

Hemos visto en este capitulo que resulta imposible comprender, o incluso crear, un ejemplo de polimorfismo sin utilizar la abstraccin de datos y la herencia. El polimorfismo es una caracterstica que no puede analizarse de manera aislada (a diferencia, por ejemplo, del anlisis de la instruccin switcli), sino que funciona de manera concertada, como parte del esquema global de relaciones de clases.

Para usar el polimorfismo, y por tanto las tcnicas de orientacin a objetos, de manera efectiva en los programas, es necesario ampliar nuestra visin del concepto de programacin, para incluir no slo los

miembros de una clase individual, sino tambin los aspectos comunes de las distintas clases y las relaciones que lienen entre si. Aunque esto requiere un esfuerzo significativo, se trata de un esfuerzo que merece la pena. Los resultados sern una mayor velocidad a la hora de desarrollar programas, una mejor organizacin del cdigo y la posibilidad de disponer de programas atnpliablcs. y un mantenimiento del cdigo ms eficiente.

Pucc enconirar l;in Mtluuone*. a lo ejercicio* seleccionado*; en el documento electrnico thi' Thinkhtg in Java AnnotuituSolutfan Guitk. li*>punibte pan la venta cu hwv.Mimillnx net.Interfaces

Las interfaces y las clases abstractas proporcionan una fomia ms estructurada de separar la interfaz de la implementacin.

Dichos mecanismos no son tan comunes en los lenguajes de programacin. C++. por ejemplo, slo tiene soporte indirecto para estos conceptos Fl hecho de que existan palabras clave del lenguaje en Java para estos conceptos indica que esas ideas fueron consideradas lo suficientemente importantes como para proporcionar un soporte directo.

En primer lugar, vamos a examinar el concepto de clase abstracta, que es una clase de trmino medio entrc una clase normal y una interfaz. Aunque nuestro primer impulso pudiera ser crear una interfaz, la clase abstracta constituye una herramienta importante y necesaria para construir clases que tengan algunos mtodos no implementados. No siempre podemos utilizar una interfaz pura.

Clases abstractas y mtodos abstractos

En todos los ejemplos de instrumentos musicales del capitulo anterior, los mtodos de la clase base Instrument eran siempre "ficticios". Si estos mtodos llegan a ser invocados, es que hemos hecho algo mal. La razn es que Instrument no tiene otro sentido que crear una interfaz comn para todas las clases derivadas de ella.

En dichos ejemplos, la nica razn para establecer esta interfaz comn es pan poder expresarla de manera diferente para cada uno de los distintos subtipos. Esa interfaz establece una forma bsica, de modo que podemos expresar todo aquello que es comn para todas las clases derivadas. Otra forma de decir esto seria decir que Instrument es una case bas abstracta, o simplemente una clase abstracta.

Si tenemos una clasc abstracta como Instrument. los objetos de dicha clase especfica no tienen ningn significado propio casi nunca. Creamos una clase abstracta cuando queremos manipular un conjunto de clases a travs de su interfaz comn. Por tanto, el propsito de Instrument consiste simplemente en expresar la interfaz y no en una implementacin concreta, por lo que no tiene sentido crear un objeto Instrument y probablemente convenga impedir que el usuario pueda hacerlo Podemos impedirlo haciendo que todos los mtodos de Instrument generen errores, pero eso retarda la informacin hasta el momento de la ejecucin y requiere que el usuario realice pruebas exhaustivas y fiables. Generalmente, resulta preferible detectar los problemas en tiempo de compilacin

Java proporciona un mecanismo para hacer esto denominado mtodo abstracto 19 Se trata de un mtodo que es incompleto: slo tiene una declaracin, y no dispone de un cuerpo. He aqui la sintaxis para la declaracin de un mtodo abstracto: abstract void fI )

Una clase que contenga mtodos abstractos se denomina clase abstracta. Si una clase contiene uno o ms mtodos abstractos. la propia clase debe Calificarse como abstrae!, (en caso contrario, el compilador generar un mensaje de error).

Si una clase abstracta est incompleta, qu es lo que se supone que el compilador debe hacer cuando alguien trate de ins- tanciar un objeto de esa clasc? El compilador no puede crear de manera segura un objeto de una clase abstracta, por lo que

19

Para lu prcgpwitadatt de O-. tnua del anlogo a I fim'innej virtuales puna de C

generar un mensaje de error. De esta forma, el compilador garantiza la pureza de la clase abstracta y no es necesario preocuparse de si se la va a utilizar correctamente.

Si definimos una clase heredada de una clase abstracta y queremos construir objetos del nuevo tipo, deberemos proporcionar definiciones de mtodos para todos los mtodos abstractos de la clase base. Si no lo hacemos (y podemos decidir no hacerlo), entonces la clase derivada sera tambin abstracta, y el compilador nos obligara a calificar ew clase con la palabra clave abstraer

Resulta posible definir una clase como abstracta sin incluir ningn mtodo abstracto Esto resulta til cuando tenemos una clase en la que no tiene sentido tener ningn mtodo abstracto y. sin embargo, queremos evitar que se generen instancias de dicha clase.

La clase Instrument del capitulo anterior puede transformarse fcilmente en una clase abstracta. Slo algunos de los mtodos sern abstractos, ya que definir una dase como abstracta no obliga a que todos los mtodos sean abstractos. He aqui el ejemplo modificado He aqui el ejemplo de la utilizar clases y mtodos //; orquesta modificado para abstractos:

interfaces/musi*/Music4.java // Clases y mtodos abstractos, package interfaces.music4 ; import polymorphism.music.Note; import static net.mindview.util.Print. / abstract class Instrument { private int i; // Storage allocated for each public abstract void playlNote n) ; public String what() ( return "Instrument; ) public abstract void adjust();

> class Wind extends Instrument { public void playlNote n) (print i"Wind.play I) " n) ; public String what IJ ( r e t u r n "Wind"; ) public void adjust I) {]

) ciass Percussion extends Instrument { public void play(Mote n] ( print("Percussion.play 0 " nJ ;

public public

String whacO ( return " Percuss ion*; void adjust 0 ()

) class Stringed extends Instrument ( public void play(Note n> ( print {"Stringed .olay I) " n) ; i public public String what 0( return "Stringed": void adjust O i) }

) class Brass extends Wind [ public void play(Noce n) { printi"Brass.play() + n)7 i j public void adjust<) { orint("Brass.adjust 0"); )

class Woodwind extends Wind f public void play(Note n) ( print("Woodwind.play() " * nj; ) public String whati) { return "Woodwind"; )

) public class Music4 (

// Wo me preocupa el tipo, por lo que los nuevos tipos // aadidos al sistema seguirn funcionando: static void tune(Instrument i) (

// ... 1.piay(Note.MIDDLE_C);

} static void LuneAll(Instrument I] e) { for(Instrument 1 : e) tune(i) ; j public static void main(StringU args) ( // Generalizacin durante la insercin en la matriz: Instrument t3 orchestra * ( new Wind O . new Percussion(I, new Stringed(), new Brass(), new Woodwind(]

). tuneAll Iorchestra); }) / Output: Wind.playl) MIDDLE_C Percussion.play() MIDDLE_C Stringed.playO MXDDLE_C Erass.play{I MIDDLE_C

Woodwind.play O MIDDLE_C *///:-

Podemos ver que no se ha efectuado ningn cambio, salvo en la clase base.

Resulta til crear clases y mtodos abstractos porque hacen que la abstraccin de una clase sea explcita, e informan tanto al usuario como al compilador acerca de cmo se pretende que se utilice esa clase Las clases abstractas tambin resultan tiles como herramientas de rediseo, ya que permiten mover fcilmente los mtodos comunes hacia arriba en la jerarqua de herencia.

Ejercicio 1: (l) Modifique el Ejercicio 9 del capitulo anterior de modo que Rodent sea una clase abstracta. Defina los

mtodos de Rodent como abstractos siempre que sea posible.

Ejercicio 2: (l) Cree una clase abstracta sin incluir ningn mtodo abstracto y verifique que no pueden crearse instan

cias de esa clase.

Ejercicio 3: (2) Cree una clase base con un mtodo print( ) abstracto que se sustituye en una clase derivada. La ver

sin sustituida del mtodo debe imprimir el valor de una variable int definida en la clase derivada. En el punto de definicin de esta variable, proporcione un valor distinto de cero. En el constructor de la clase base, llame a este mtodo. En main( ). cree un objeto del tipo derivado y luego invoque su mtodo print( ) Explique los resultados.

Ejercicio 4: (3) Cree una clase abstracta sin mtodos Defina una clase derivada y adale un mtodo. Cree un mto

do esttico que tome una referencia a la clase base, especialcelo para que apunte a la clase derivada e invoque el mtodo. En mnin( ), demuestre que este mecanismo funciona. Ahora, incluya la declaracin abstracta del mtodo en la clase base, eliminando asi la necesidad de la especializacin Interfaces

La palabra clave interface lleva el concepto de abstraccin un paso ms all. La palabra clave abstract permite crear uno o ms mtodos no definidos dentm de una clase: proporcionamos parte de la interfaz, pero sin proporcionar la implementa- cin correspondiente. La implementacin se proporciona de las clases que hereden de la clase actual. La palabra clave interface produce una clase completamente abstracta, que no proporciona ninguna implementacin en absoluto. Las interfaces permiten al creador determinar los nombres de los mtodos, las listas de argumentos y los tipos de retomo, pero sin especi ficar ningn cuerpo de ningn mtodo. Una interfaz proporciona simplemente una forma, sin ninguna implementacin.

Lo que las interfaces hacen es decir: Todas las clases que implementen esta interfaz concret tendrn este aspecto Por tanto, cualquier cdigo que utilice una interfaz concreta sabr qu mtodos pueden invocarse para dicha interfaz y eso es todo. Por tanto, la interfaz se utiliza para establecer un protocolo entre las clases (algunos lenguajes de programacin orientados a objeto* disponen de una palabra clave denominada protocol para hacer lo mismo).

Sin embargo, una interfaz es algo ms que simplemente una clase abstracta llevada hasta el extremo, ya que permite realizar una variante del mecanismo de herencia mltiple creando una clase que pueda generalizarse a ms de un tipo base.

Para crear una intcraz. utilice la palabra clave interface en lugar de class. Al igual que con una clase, puede aadir la palabra clave public antes de interface (pero slo si dicha interfaz est definida en un archivo del mismo nombre). Si no incluimos la palabra clave public. obtendremos un acceso de tipo paquete, porque la interfaz slo sera utilizable dentro del mismo paquete. Una interfaz tambin puede contener campos, pero esos campos sern implcitamente de tipo static y final

Para definir una clase que se adapte a una interfaz concreta (o a un grupo de interlaces concretas), utilice la palabra clave implements que quiere decir La interfaz especifica cul es el aspecto, pero ahora vamos a decir cmo funciona. Por lo dems, la definicin de la clase derivada se asemeja al mecanismo normal de herencia. El diagrama para el ejemplo de los instrumentos musicales seria el siguiente: Podemos ver en las clases una vez que hemos la implementaein pasa a puede ampliarse de la Woodwind y Brass que implementado la interfaz, ser una clase normal que forma usual

Podemos declarar de una interfaz como sern pblicos an cuando tanto, cuando interfaz, los mtodos de esa definidos como pblicos. revenirla de forma de tipo paquete, con lo que accesibilidad de los herencia, cosa que el compilador de Java no permite.

explcitamente los mtodos public. pero esos mtodos no lo especifiquemos. Por implementemos una interfaz deben estar En caso contrario, se predeterminada ai acceso estaramos reduciendo la mtodos durante la

Podemos ver esto en la versin modificada del ejemplo Instrument. Observe que todos los mtodos de la interfaz son estrictamente una declaracin, que es lo nico que el compilador permite. Adems,

ninguno de los mtodos de Instrument se declara como public. pero de lodos modos son pblicos de manera automtica: //: interfaces/music5/Music5.java // Interfaces. paclcage interfaces .music5; mport polymorphism.music.Note; import static net . mindview.util .Print. ; interface Instrument { // Constante de tiempo de compilacin: int VALUE = 5; // static & final // Ko puede tener definiciones de mtodos: void play(Note n); // Automticamente pblico vola adjustf);

) class Wind implements Instrument { public void play(Note n) { ) printlthis * H M.play{) " n) ;

public Strng toStringl) ( retum "Wind"; ) public void adjust() { printlthis ' .adjust () ") ; } class Percu9 sion mpiementa Instrument ( public void playiNote ni { printlthis -* ".playO " -r n>; i public String toStringO { return "Percussion"; ) public void adjustO ( print (this - ".adjust (J *) : ) I class Stringed implements Instrument { public void playiNote n) { print (this -f M.play() + n) ;

) public String toStringO { return "Stringed"; ) public void adjust0 { print(this .adjust0 "); }

} class Brass extends Wind { oublic String toStringO ( return "Brass"; | j class Woodwind extends Wind ( oublic String toStringO { return "Woodwind"; ) I public class MusicS { // No le preocupa el tipo, por lo que los nuevos tipos // que se aaden al sistema seguirn funcionando: static void tune(Instrument il { // . . . i.play(Note.MIDDLE_C);

) static void tuneAll(Instrument[] e) { for(Instrument i : e) tune(i);

} public static void mainIString[] args) (

// Generalizacin durante la insercin en la matriz: Instrument[) orchestra = ( new Wind(), new PercussionO, new Stringed O. new Brass f) , new Woodwind0 tuneAll(orchestra)

) } / Output; Wind.playf) MIDDLE C Percussion.play() MIDDLE_C Stringed. playO MIDDLE_C Brass.playO MIDDLE_C Woodwind.play O MIDDLE_C *///:-

En esta versin del ejemplo liemos hecho otro cambio; el mtodo what( ) ha sido cambiado a toStrlng ), dado que esa era la forma en que se estaba utilizando el mtodo. Puesto que loStrngt ) forma parte de la clase raz Ohject, no necesita aparecer en la interfaz. El resto del cdigo funciona de la misma manera. Observe que no importa si estamos generalizando a una clase normal* denominada Instrument. a una clase abstracta llamada Instrument. o a una interfaz denominada Instrument. El compor

9 Interfaces 545 *taimente es siempre el mismo. De hecho, podemos ver en el mtodo tunet ) que no existe ninguna evidencia acerca de si

Instrument es una clase normal**, una clase abstracta o una interfaz.

Ejercicio 5: (2) Cree una interfaz que contenga tres mtodo* en su propio paquete. Implemento la interfaz en un paque

te diferente.

Ejercicio 6: (2) Demuestre que todos los mtodos de una interfaz son automticamente pblicos.

Ejercicio 7: (1) Modifique el Ejercicio 9 del Capitulo 8. Polimorfismo, para que Rodent sea una interfaz

9 Interfaces 546 Ejercicio 8: (2) En polymorphism.Sandwich.javu. cree una interfaz denominada FastFood (con los mtodos apro

piados) y cambie Sandwich de modo que tambin implemente FastFood.

Ejercicio 9: (3) Redisee Music5.java mov iendo los mtodos comunes de W'ind. Percussion y Stringeda una clase

abstracta.

Ejercicio 10: (3) Modifique Music5.java aadiendo una interfaz Playahle. Mueva la declaracin de p!ay( ) de

9 Interfaces 547 Instrument a Playahle. Aada Playahle a las clases derivadas incluyndola en la lista implements Modifique tune( ) de modo que acepte un objeto Playahle en lugar de un objeto Instrument. Desacoplamiento completo

Cuando un mtodo funciona con una clase en lugar de con una interfaz, estamos limitados a utilizar dicha clase o sus subclases. Si quisiramos aplicar ese mtodo a una clase que no se encontrara en esa jerarqua, no podramos. Las interfaces relajan esta restriccin considerablemente. Como resultado, permiten escribir cdigo ms reutili/able.

Por ejemplo, suponga que disponemos de una clase Proeessor que tiene sendos mtodos ame ( ) y process( ) que toman una cierta entrada, la modifican v generan una salida. La clase base se puede ampliar para crear diferentes tipos de objetos Proeessor. En este caso, los subtipos de Proeessor modifican objetos de tipo String (observe que los tipos de retomo pueden ser cov ariantes, pero no los tipos de argumentos): //: mnerfaces/classprocessor/Appiy.java package interfaces.classprocessor; impart java.til. *, mport static nec .mindview. ucil, Prini. *; class Proeessor { public String ame( ( retom getCiass11.getSimpleName();

9 Interfaces 548 ) Objecc process(Object input) | retum input; }

) class Upcase extends Proeessor { String process (Object inputI ( // Retomo covariante retum ((String) input).toUppervasel)

) } class Downcase extends Processor { String process(Object input) ( return {(Strinci) input) . toLowerCase() ;

9 Interfaces 549 class Splicter extends Processor { String process(Object inputi ( I I El m^Lodo split 0 divide una cadena er* fraamentos: retum Arrays.toString(((String)input).split(* "));

) public class Apply { public scatic void process(Processor p. Obnect s) ( print IUsing Processor M 4 p.name O J; print Ip.process(s>);

) public static String s = wDisagreement with beliefs is by definition incorrect"; public static void main(String(J args< { process(new UpcaseO4 s); process(new Downcase(), s) ; processnew Splitter O, s) ;

) ) / Output: Using Processor Upcase DISAGREEMENT WITH BELIEFS IS BY DEFINITION

9 Interfaces 550 INCORRECT Using Processor Downcase disagreement with beliefs is by definition incorrect Using Processor Splitter [Disagreement, with, beliefs, is, by, definition, incorrect]

///:-

El mctodo Apply.process( ) toma cualquier Upo de objeto Processor y lo aplica a un objeto Object, imprimiendo despus los resultados La creacin de un mtodo que se comporte de forma diferente dependiendo del objeto argumento que se le pase es lo que se denomina el patrn de diseo basado en estrategias. El mtodo contiene la parte fija del algoritmo que hay que impJcmentar. mientras que la estrategia contiene la parte que varia. La estrategia es el objeto que pasamos, y que contiene el cdigo que hay que ejecutar. Aqu, el objeto Processor es la estrategia y en man( ) podemos ver como se uplican tres estrategias diferentes a la cadena de caracteres v

El mtodo splitC ) es parte de la clase String; toma el objeto String y lo divide utilizando el argumento como frontera, v devolviendo una matriz String|| Se utiliza aqu como forma abreviada de crear una matriz de objetos String.

Ahora suponga que descubrimos un conjunto de filtros electrnicos que pudieran encajar en nuestro mtodo Appl\.process( )

9 Interfaces 551 //: mterfaces/filters/Waveform.java package interfaces.filters; public class Waveform ( private static long counter; private final long id = counter--*; public String toStringO f return "Waveform " id; )

) ///:I t : interfaces/filters/Filter.Java package interfaces.filters; public class Filter ( public String name 1) { return getClass f).getSimpleName I)j

} public Waveform process(Waveform input) { return input; } } m-.~ //: interfaces/filters/LowPass.java package interfaces.filters; public class LowPase extends Filter { double cutoff; public LowPass(double cutoff) { this.cutoff = cutoff; ) public Waveform process(Waveform input > ( return input; // Dummy processing

9 Interfaces 552 )

} ///://: interfaces/filners/HighPass.jav a package interfaces.fiIters; public class HighPaas extends Filter { double cutoff; public cutoff; public { return input; HighPass(double cutoff { } Waveform process(Waveform ) this.cutoff input)

) ///:/; interfaces/fliters/BandPass.jav a package interfaces.filters; public class BandPass extends Filter { double lowCutoff. highCutoff; public BandPass(double lowCut, double highCut) { lowCutoff lowCut; highCutoff = highCut; i public ( retum input; Waveform process(Waveform ) input)

9 Interfaces 553 | ///!-

Killer licne los mismos elementos de interfaz que Processor, pero puesto que no hereda de Proeessor (puesto que el creador de la clase Filler no tena ni idea de que podramos querer usar esos objetos como objetos Processor), no podemos utilizar un objeto Filter con el mtodo Apply.process( ). a pesar de que funcionara. Bsicamente, el acoplamiento entre Apply.process( ) y Proeessor es ms fuerte de lo necesario y esto impide que el cdigo de Apply.process() pueda sutilizarse en lugares que seria til. Observe tambin que las entradas v salidas son en ambos casos de tipo Waveform.

Sin embargo, si Processor es una interfaz, las restricciones se relajan lo suficiente como para poder reutilizar un mtodo Apply.proccss( ) que acepte dicha interfaz. He aqui las versiones modificadas de Proeessor v Apply: : interfaces/mterfaceprocessor/Processor . java package interfaces.interfaceprocessor; public interface Processor ( String name Oj Object process{Object input); ) U h: : interfaces/mterfaceprocesscr/Apply . java package interfaces.interfaceprocessor; import static net.mindview.til.Print.; public class Apply { public static void processiProeessor p, Object s} ( print ("Using Processor " * p.namel)); print(p.orocess(si I;

9 Interfaces 554 ) I ///:-

La primera forma en que podemos reutilizar el cdigo es si los programadores de clientes pueden escribir sus clases para que se adapten a la interfaz, como por ejemplo: i /: interfaces/interfaceprocessor/StringProces sor.java package interfaces.nterfaceprocessor; import java.util.*; public abstract class StringProcessor implements Processor) public String rame0 ( i return getClass(J .getSimpleName l J;

public abstract String process(Object input); public static String s =

555 Piensa en Java "If she weighs the same as 3 duck, she's made of wood";public static void main(String(] argsl { Apply, process (new Upcase(), s) j Apply.process (new Downcase (), s) Apply .process (new Splitterd, s) j ) I class Upcase extends StringProcessor { public String process (Object input) { // Recomo covariante return ( (String)input).toUpperCase<)/

>

1 class Downcase extends StringProcessor ( public String process(Object input) ( return {(String)input).t oLowerCase(); class Splitter extends StringProcessor ( public String process(Object input! ( return Arrays. toString ( ( i String) lr.DUt) .split (" " M ; I

556 Piensa en Java I ) / Output: Using Processor Upcase IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD Using Processor Downcase if she weighs the same as a duck, shes made of wood Using Processor Splitter [If, she, weighs, the, same, as, a. duck., she's, made. of. wood]

///:-

Sin embargo, a menudo nos encontramos en una situacin en la que no podemos modificar las clases que queremos usar. En el caso de los filtros electrnicos, por ejemplo, la correspondiente biblioteca la liemos descubierto, en lugar de desarrollarla. En cslos casos, podemos utilizar el patrn de diseo adaptador. Con dicho patrn de diseo, lo que hacemos es escribir cdigo para tomar la interfaz de la que disponemos y producir la que necesitamos, como por ejemplo: //: interfaces/interfaceprocessor /FilterProcessor.java package interfaces.interfaceprocessor ; import interfaces.filters.*; class FilterAdapter implements Processor ( Filter filter; public FilterAdapter(Fil ter filter) ( this.filter * filter; I

557 Piensa en Java ) public String name () ( return filter, name (I ,* J public Waveform process(Object input) ( return filter.process((Waveform) input);

) 1 public class FilterProcessor { public static void main(StringU args) { Waveform w * new Waveform!); Apply.process(new FilterAdapter(new LowPass(1.0)), w); Apply.process(new FilterAdapterInew HighPass(2 .01), w); Apply.process( new FilterAdapter(new Bandpass(3.0, 4.0)), w) ;} ( Output Using Processor LowPass Waveform 0 Using Processor HighPass Waveform 0 sing Processor BandPass Waveform 0 ///;-

558 Piensa en Java En esta aplicacin, el patrn de diseo, de adaptacin, el constructor FilterAdaptcr. toma la interfaz que tenemos (Fiiter) v produce un objeto que tiene la interfaz Processor que necesitamos. Observe tambin la utilizacin del mecanismo de delegacin en la clase FilterAdaptcr

Desacoplar la interfaz de la implementacin permite aplicar las interfaces a mltiples implementaciones diferentes, con lo que el cdigo es ms reutilizable.

Ejercicio 11: (4) Cree una clase con un mtodo que tome como argumento un objeto String y produzca un resultado en

el que se intercambie cada pareja de caracteres contenida en el argumento. Adapte la clase pan que funcione con interfaceproccssor.Apply.process( ).

559 Piensa en Java Herencia mltiple en Java

Puesto que una interfaz no dispone de implementacin (es decir, no hay ningn almacenamiento asociado con una interfaz) no hay nada que impida combinar varas interfaces. Esto resulta muy til en ocasiones, como por ejemplo cuando queremos implementar el concepto una \ es una a y una b y una c" En C++, este acto de combinar mltiples interfaces de clase se denomina herencia mltiple. y puede llegar a resultar muy completo, porque cada clase puede tener una implementacin. En Java, podemos hacer lo mismo, pero slo una de las clases puede tener una implementacin, por lo que los problemas de C-t-f no aparecen en Java cuando se combinan mltiples interfaces: En una derivada, estamos a lencr base que abstracta concreta no tenga clase no obligados una clase sea o (una que mtodos

abstractos) Pero si realizamos la herencia de algo que no sea una interfaz, slo podemos heredar de una de esas clases: los restantes elementos base debern ser interfaces. Hay que colocar todos los nombres de interfaz detrs de la palabra clave mplements y separarlos mediante comas. Podemos incluir tantas interfaces como queramos y podemos realizar generalizaciones [upenst) a cada interfaz, porque cada una de esas interfaces representa un tipo independiente. El siguiente ejemplo muestra una clase concreta que se combina con vanas interfaces para producir una nuev a clase: f /: interfaces/Adventure-java // Interfaces mltiples. interface CanFight ( void fightl);

560 Piensa en Java } interface CanSwim ( void swim0 /

} interface CanFly { void fly(); i class ActionC haracte r ( publi c void fightt) ()

) class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly ( public void svrimO (} public void fly O ()

561 Piensa en Java ) public class Adventur ( publicstaticvoid { x..fightt) ) publicstaticvoid ( x.swim()j) publicstaticvoid x.fly(); ) publicstaticvoid args) { t(h);//Treatitas uh);//Treatitas v(h);//Treatitas w(h);//Treatitas t(CanFight ulCanSwim vfCanFly x) x) x) {

publicstaticvoid w(ActionCharacter xi { x.fight(); ) raain(String[]

Hero h * new Hero(); a CanFight a CanSwim a CanFly an ActionCharacter

) ///:-

562 Piensa en Java Puede ver que Mero combina la clase concreta ActionCharacter con las interfaces CanFight. CanSwim y CanFly. C uando se combina una dase concreta con interfaces de esta forma, la clase concreta debe expresarse en primer lugar y las interfaces indicarse a continuacin (en caso contrario, el compilador nos dar un error).

La signatura de fighf( ) es igual en la interfaz CanFight y en la clase AetionC'haracter Asimismo, a fight ) no se le proporciona una definicin en Hero. Podemos ampliar una interfaz, pero lo que obtenemos entonces ser otra interfaz. Cuando queramos crear un objeto, todas las definiciones debern haber sido ya proporcionadas Aunque Hero 110 proporciona explcitamente una definicin para figlt( ). dicha definicin est incluida en ActionCharacter: por tanto, es posible crear objetos Hero

En la clase Adventure. podemos ver que hay cuatro mtodos que toman argumentos de las distintas interfaces y de la clase concreta Cuando se crea un objeto Hero. se le puede pasar a cualquiera de estos mtodos, lo que significa que estar siendo generalizado en cada caso a cada una de las interfaces. Debido a la forma en que se disean las interfaces en Java, este mecanismo funciona sin que el programador tenga que preocuparse de nada.

Recuerde que una de las principales razones para utilizar mierfaces es la que se ilustra en el ejemplo anienor: para realizar generalizaciones a ms de un tipo base (y poder disfrutar de la flexibilidad que esto proporciona). Sin embiirgo. una segunda razn para utilizur interfaces coincide con la razn por la que utilizamos clases base abstractas: para impedir que el programador de clientes cree un objeto de esta clase y para establecer que slo se trata de una interfaz. I

563 Piensa en Java Esto hace que surja una cuestin: debemos utilizar una interfaz o una clase abstracta? Si resulta posible crear nuestra clase base sin ninguna definicin de mtodo y sin ninguna variable miembro, siempre son preferibles las interfaces a las clases abstractas. De hecho, si sabemos que algo va a ser una clase base, podemos considerar si resultara conveniente transformarla en interfaz {hablaremos ms sobre este tema en el resumen del captulo).

Ejercicio 12: (2) EnAdventure.java.aada una siguiendo el patrn de las otras interfa

interfaz

llamada

CanClimb.

ces.

Ejercicio 13: (2)Creeunainterfaz yherede de interfaces. Defina, mediante herencia mltiple, una

ella

otras

dos

nuevas

tercera interfaz a partir de estas otras dos.: I

564 Piensa en Java - Este ejemplo muestra cOuno tas interfaces e\itan el denominado "problema del rombo que ve pre-vnita en el mecanismo de herencia mlliplc de O* Ampliacin de la interfaz mediante herencia

podemos aadir fcilmente nuevas declaraciones de mtodos a una interfaz utilizando los mecanismos de herencia, y tambin podemos combinar varias interfaces mediante herencia para crear una nueva interfaz. En ambos casos, obtendremos una interfaz nueva, como se ve en el siguiente ejemplo: //: interfacea/HorrorShow.java // Ampliacin de una Interfaz mediante herencia. interface Monster { void menace Mj

) interface DangerousMonster extends Monster { void destroy(); i interface Lethal { void killO; i class DragonZilla implements DangerousMonster ( public void menace{) {} public void destroy0 {) i interface Vampire extends DanaerousMonster, Lethal { void drinkBlood () ,* I

565 Piensa en Java ) class VeryBadVampire implements Vampire { public void menace() I) public void destroy 0 () public void kill 0 {) public void drinkBlood() {) I public class HorrorShow ( static void u(Monster b) ( b.menaced; J static void v(DangerousMonster d) { d.menace(); I
d.

destroy 0 ;

static void w(Lethal 1) { l.killO; ) public static void main(Stringf] argsJ ( DangerousMonster barney = new DragonZilla{); u(barney); v(barney); Vampire vlad * new VeryBadVampire () ; U ( v l a d ) ; v ( v l a d ) , * w i v I

566 Piensa en Java l a d } ;

) ) ///:-

DangerousMonster es una extensin simple de Monster que produce una nueva interfaz Esta se implementa en DragonZilla

La sintaxis empleada en Vampiro slo funciono cuando se heredan interfaces. Normalmente, slo podemos utilizar e\tends con una nica clase, pero extends puede hacer referencia a mltiples interfaces base a la hora de construir una nueva interfaz Como puede ver, los nombres de interfaz est simplemente separados por comas

Ejercicio 14: (2) Cree tres interfaces, cada una de ellas con dos mtodos Defina I

567 Piensa en Java mediante herencia una nueva interfaz

que combine las tres, aadiendo un nuevo mtodo. Cree una clase implementando la nueva interfaz y que tambin herede de una clase concreta. A continuacin, escriba cuatro mtodos, cada uno de los cuales tome una de las cuatro interfaces como argumento. En main ). cree un objeto de esa clase y pselo a cada uno de los mtodos.

Ejercicio 15: (2)Modifiqueelejercicioanterior haciendo que la clase derivada herede de

creando

una

clase

abstracta

ella. Colisiones de nombres al combinar interfaces

Podemos encontramos con un pequeo problema a la hora de implementar mltiples I

568 Piensa en Java interfaces En el ejemplo anterior, tanto CaiiFight como ActionC'haracter tienen sendos mtodos idnticos void fight( ). El que haya dos mtodos idnticos no resulta problemtico, pero t>qu sucede si los mtodos difieren en cuanto a signatura o en cuanto a tipo de retomo? He aqui un ejemplo: //: interfaces/Interfac eCollision.java package interfaces; class C ( public int f() ( return 1; } ) class C2 implements II, 1 2 ( public void f() {) public nt f<int i) ( sobrecargado return 1; } //

) class C3 extends C iraplements 12 ( public int flint i) ( sobrecargado return 1? ) //

) class C4 extends C implements 13 { // Idntico. No hay problema: public int

569 Piensa en Java fi) ( return 1; )

) // Los mtodos slo difieren en el tipo de retorno: / / i class C5 extends C implements II {) / / i interface 14 extends II. 13 {} ///:-

La dificultad surge porque los mecanismos de anulacin, de implementacin y de sobrecarga se entremezclan de forma compleja. Asimismo, los mtodos sobrecargados no pueden diferir slo en cuanto al tipo de retomo Si quitamos la marca de comentario de las dos ltimas lineas, los mensajes de error nos informan del problema InterfaceCoUision.java: 23; ft ) in C cannot implementff I in II. altempting to use incompatible return type found: int requtred; void interfaceColtision. java:2<f; Interfaces 3 and II are incompatible; lu>tli define f' ). but wiih different return type

Asimismo, utilizar los mismos nombres de mtodo en diferentes interfaces que vayan a ser combinadas suele aumentar, generalmente, la confusin en lo que respecta a la legibilidad del cdigo Trate de evitar la utilizacin de nombres de mtodo idnticos.

570 Piensa en Java Adaptacin a una interfaz

Una de las razones mas importantes para utilizar interfaces consiste en que con ellas podemos disponer de mltiples imple- mentaciones para una misma interfaz, tn los casos ms simples, esto se lleva a la prctica empleando un mtodo que acepta una interfaz, lo que nos deja total libertad v responsabilidad para implementar dicha interfaz y pasar nuestro objeto a dicho mtodo.

Por tanto, uno de los usos ms comunes para las interfaces es el patrn de diseo basado en estrategia del que >a hemos hablado: escribimos un mtodo que realice ciertas operaciones y dicho mtodo toma como argumento una interfaz que especifiquemos. Bsicamente, lo que estamos diciendo es: Puedes utilizar mi mtodo con cualquier objeto que quieras, siempre que este se adapte a mi interfaz". Esto hace que el mtodo sea ms flexible, general y reutilizable.

Por ejemplo, el constructor para la clase Scanner de Java SE5 (de la que hablaremos ms en detalle en el Capitulo 13. Cadenas de caracU'tvs) admite una interfaz Readable. Como veremos. Readable no e> un argumento de ningn otro mtodo de la biblioteca estndar de Java, fue creado pensando especficamente en Scanner, de modo que Scanner no tenga que restringir su argumento para que sea una clase determinada. De esta forma, podemos hacer que Scanner funcione con ms tipos de datos Si creamos una nueva clase y queremos poder usarla con Scanner, basta con que la hagamos de tipo Readable, como por ejemplo: //: interfaces/RanaomWords.java I

571 Piensa en Java // Implementation de una interfaz para adaptarse a un mtodo - import java.nio.*; import java.util.*; public class RandomWords implements Readable { private static Random rand new Random(47); private static final chart] capitals = "ABCDEFGHIJKLMNOPQRSTU VWXYZ- . toCharArray() private static final chart] lowers = 'abcdetghijkimnopqrst uvwxys''. toCharArray ( ) ; private static final chart] vowels = "aeiou".toCharArray(); private lnt count; public RandomWords(int count) ( this.count * count; J public int readiCharBuffer cb) ( if(count-- == 0) return -1; // Indic3 el final de la entrada cb.append I capitalsIrand.nextInt(capit als.length) ] ) ; for(int i * O; i < 4; i*-) ( cb.append(vowels[rand.nextI nt(vowels.length)]); cb.append(lower8 [rand.nextI nt[lowers.length)]); I cb.append (M ); i return 10 ? // NQmero de caracteres aadidos

public static void main(String[J args> { Scanner s = new Scanner(new RandomWords1 10)); while(s.hasNext()) System.out.printIn(s.next())/

572 Piensa en Java ) } /* Output: Y a z e r u y a c P o w e n u c o r G e e a z i m o m R a e u u a c i o N u o a d e s i w H I

573 Piensa en Java a g e a i k u x R u q i c i b u i S u m a s e t i h Kuuuuozoa Waqizeyoy

*///:-

574 Piensa en Java La interfaz Readable slo requiero que se implemento un mtodo read( Dentro de read( la informacin al ).

argumento CharBufTcr (hay varias formas de hacer esto, consulte la documentacin deCharBuffer). devolvemos -\

cuando ya no haya ms datos de entrada.

Supongamos que disponemos de una clase base que an no implementa Readable, en este caso, cmo podemos hacer que funcione con Scanner He aqu un ejemplo de una clase que genera nmeros en coma flotante aleatorios. //: interfaces/Ran doraDoubles.ja va import java.util.*; public clase RandomDoubles { private static Random rand = new Randomi47); public double next() { return rand.nextDouble0 ; ) public static void main(String[] args) I

575 Piensa en Java ( RandoraDoubles rd = new RandomDoubles( I ; for(int i 0; i < 7; i ++) i


0. 0.

System.out.print(rd.next{ ) t " " ) /

) /* Output: 7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 5166020801268457 0.2613610344283964 0 .2678662084200585

*///:-

De nuevo, podemos utilizar el patrn de diseo adaptador, pero en este caso la clase adaptada puede crearse heredando e implementando la interfaz Readable. Por tanto, si utilizamos la herencia pseudo-mltiple proporcionada por la palabra clave interface, produciremos una nueva clase que sera a la v ez RandomDoubles y Readable: //: interfaces/AdaptedRandomDoubles .java // Creacin de un adaptador mediante herencia, import java.nio.*/ import java. uni 1 . * public class AdaptedRandomDoubieo extends RandoraDoubles implements Readable ( private int count; I

576 Piensa en Java public AdapteoRandomDoubies(int count} ( this.count > count; i public int read{CharBuffer cb) ( if(count-- 0 ) return -1 ; String result = Double. toString (next 0 J - " "; cb.append(result); return result.length();

) public static void main(StringU args> { Scanner s = new Scanner(new AdaptedflandomDoubles(7) ) while(s.hasNextDouble()) System.out .print Is .nextDouble ( I " *);

) ) / Output: 7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732


0. 0.

5166020801268457 0.2678662084200585 0.2613610344283964 ///*-

577 Piensa en Java Puesto que podemos aadir de esta forma una interfaz a cualquier clase existente, podemos deducir que un mtodo que lome como argumento una interfaz nos permitir adaptar cualquier clase para que funcione con dicho mtodo. Aqui radica la verdadera potencia de utilizar interfaces en lugar de clases

Ejercicio 16: (3)Creeunaclasequegenereunasecuenciade para que pueda utilizarse

caracteres. Adapte esta clase

como entrada a un objeto Scanner Campos en las interfaces

Puesto que cualquier campo que incluyamos en una interfaz sera automticamente de tipo static y final, la interfaz constituye una herramienta conveniente para crear grupos de valores constantes. Antes de Java SE5, sta era la nica forma de producir el mismo efecto que con la palabra clave enum en C o C++. Por tanto, resulta habitual encontrarse con cdigo anterior a la versin Java SE5 que presenta el aspecto siguiente: f / \ interfaces/MonLhs.java // Uso de interfaces para crear grupos de constantes. package interfaces; public interface Months ( int I

578 Piensa en Java JANUARY = 1, FEBRUARY = 2 , MAP.CH * 3, APRIL = 4, MAY 5. JUNE * 6 . JULY * 7, AUGUST = 8 , SEPTEMBEP. 9, OCTOBER = 10, NOVEMBER = 11, DECEMBER 12;

} ///r-

Observe la utilizacin del estilo Java, en el que todas las letras estn en maysculas (con guiones bajos para separar las distintas palabras que formen un determinado identificador) en los casos de valores estticos finales con inicializadores constantes. Los campos de una interfaz son automticamente pblicos, asi que el atributo public no se especifica explcitamente.

Con Java SE5. ahora disponemos de la palabra clave enum. mucho ms potente y flexible, por lo que rara vez tendr sentido que utilicemos interfaces para definir constantes. Sin embarco, quiz se encuentre en muchas ocasiones con sta tcnica antigua a la hora de leer cdigo heredado (los suplementos de este libro disponibles en MindVtew.net proporcionan una descripcin completa de la tcnica previa a Java SE5 para producir tipos enumerados utilizando interfaces). Puede encontrar ms detalles sobre el uso de la palabra clave enum en el Captulo 19. Tipo* enumerados

579 Piensa en Java Ejercicio 17: (2) Demuestre que los campos de una interfaz son implcitamente de tipo static y final Inicializacin de campos en las interfaces

Los campos definidos en las interfaces no pueden ser valores finales en blanco", pero pueden miciatizarse con expresiones no constantes. Por ejemplo: //J interfaces/RandVals.java // Inicializacin de campos de interfaz con // inicializadores no constantes, lmport java.util.*; public interface RandVais ( Random RAND new Random(47); int RANDOM__INT = RAND.nextlnt<10) ; long RANDOM_LONG = RAND,nextLcng() 10; float RANDOM_FLOAT * RAND.nextLong{) 10; double RANDOMJDOUBLE = RAND.nextDouble() * 10; ) ///:-

Puesto que los campos son estticos, se inicializan cuando se carga por primera vez la clase, lo que tiene lugar cuando se accede por primera vez a cualquiera de los campos. He aqu una prueba simple: //: interfaces/TestRandVals.java mport static net.mindview.util.Print.*; I

580 Piensa en Java public clase TestRandVals ( public static void main(Strin g 11 args) { print RandVals.R AND0M_INT) ; print(Rand Vals.RANDQ M_LONG); print(Rand Vals.RAND0 M_FL0A7); print Rand Vals.RANDO M DOUBLE/ j i ) / Output:

B 3 2 0 3 2 2 4 7 0 1 6 5 5 9 9 5 4 9

581 Piensa en Java . 5 9 3 9 2 9 1 E 1 8 5 . 7 7 9 9 7 6 1 2 7 8 1 5 0 4 9 '/ // :-

Los campos, por supuesto, no forman parte de la interfaz. Los valores se almacenan en el rea de almacenamiento esttico correspondiente a dicha interfaz.

582 Piensa en Java Anidamiento de interfaces

Las interfaces pueden anidarse dentro de clases y dentro de otras interfaces.3 Esto nos revela una serie lie caractersticas interesantes: //: interfaces/nesting/Ne stinglnterfaces.java package interfaces.nesting; class A { interface B ( void ft);

) public class BImp implements B f public void f() (}

) I

583 Piensa en Java private class BImp2 implements B { public void f() {)

} public interfa ce C { void f (); i class CImp impleme nts C ( public void f() (}

) private class CImp2 implements C ( oublic void fO ()

584 Piensa en Java ) private interfa ce D { void f();

} private class DImp implements D ( public void f() ()

) public class Dlmp2 implements D { public void f() ()

] public D getDO { return new DImp2(); } I

585 Piensa en Java private D dRef; public void receiveD{D d) { dRef = d; dRef.f()

9 Interfaces 586 ;}

) interface E ( interfac e G ( void f I)i J f ( public" redundante: public interface K { void f|; l void g(); // No puede ser private dentro de una interfaz: //i private interface I ()

) public class Nestinglnterfaces ( public class BImp implements A.B { public void ft) {)

) class CImp implements A.C ( oublic void f() {}

9 Interfaces 587 } // No se puede implementar una interfaz privada excepto f t dentro de la clase defintoria de dicha interfaz: //J class DImp implements A.D { //! public void ft) {( f/l I class EImp implements E ( public void gO {)

class EGImp implements E.G ( public void ft) ()

} class EImp2 implements E { public void gO {) class EG implements E.G ( public void f() (}

) public static void main(Strlng[] argsj ( k = - new A () ; t f No se puede acceder a A.D: //l A.D ad * a.getDO; // Slo puede devolver a A.D: f t I A .DImp 2 di2 a.getDO;

9 Interfaces 588 f t No se puede acceder a un miembro de la interfaz: //i a.getDO.f O; f t Slo otra A puede utilizar getDO: A a2 = new At) ; a2 .receiveD(a.getD());

>

} ///:-

Ui sintaxis para anidar una interfaz dentro de una clase es razonablemente amplia. Al igual que las interfaces no anidadas, las anidadas pueden tener visibilidad pblica o con acceso de paquete.

Como caracterstica adicional, las interfaces tambin pueden ser privadas, como podemos ver en A.D (se necesita la misma siniaxis de cualificacin para las interfaces anidadas que para las clases anidadas). Para qu sirve una interfaz anidada privada? Podemos suponer que slo puede implementarse como clase interna privada, como en DImp. pero A.DImp2 mus- ira que tambin puede implementarse como clase pblica. Sin embargo. A.DImp2 slo puede utilizarse como ella misma. No se nos permite mencionar el hecho de que implementa la interfaz privada D. por lo que implementor interfaces privadas es una forma de forzar la definicin de los mtodos de dicha interfaz sin aadir ninguna informacin de tipos (es decir, sin permitir ninguna generalizacin).

9 Interfaces 589 ti mtodo getD( ) nos revela un dato adicional acerca de las interfaces privadas: se trata de un mtodo pblico que devuelve una referencia a un interfaz privada. Que podemos hacer con el valor de retomo de este mtodo? En main( ). podemos ver varios intentos de utilizar el valor de retomo, todos los cuales fallan. La nica cosa que funciona es entregar el valor de retomo a un objeto que tenga permiso para usarlo, que en este caso es otro objeto A. a travs del mtodo receiveD( ).

La interfaz E muestra que podemos anidar unas interfaces dentro de otras. Sin embargo, las reglas acerca de las interfaces, en particular, que todos los elementos de la interfaz tienen que ser pblicos, se imponen aqu de manera estricta, por lo que una interfaz anidada dentro de otra ser automticamente pblica y no puede nunca definirse como privada.

Nostinglnterfaces muestra las diversas formas en que pueden implementarse las interfaces anidadas. En particular, observe que. cuando iniplementamos una interfaz, no estamos obligados a implcmentar ninguna de las interfaces anidadas dentro de ella. Asimismo, las interfaces privadas no pueden implementarse fuera de sus clases definitorias.

Inicialmente, pudiera parecer que estas caractersticas slo se hubieran aadido para garantizar la coherencia sintctica, pero mi experiencia es que una vez que se conoce una caracterstica siempre se descubren ocasiones cu las que puede resultar til

9 Interfaces 590 Interfaces y factoras

El objeto principal de una interfaz es permitir la existencia de mltiples implcmentaciones. y una forma tpica de producir objetos que encajen con una interfaz, es el denominado patrn de diseo de mtodo factora. En lugar le llamar a un constructor directamente, invocamos un mtodo de creacin en un objeto factora que produce una implementacin de la interfaz: de esta forma, en teora, nuestro cdigo estar completamente aislado de la implementacin de la interfaz, haciendo asi posible intercambiar de manera transparente una implementacin por otra. He aqu un ejemplo que muestra la estructura del mtodo factora: //: interf aces/Factories .-java import static net .mindview.til. Print ,* interface Service ( void methodl( ) j void method2 (); i interface ServiceFactory ( Service getService); i class Implementation.1 implements Service { Implementationl() {| // Package access public void methodli) (printi"Implementationl methodl"!;} public void method2<J {print < "Implementationl method2"J;}

) class mplementationlFactory implements ServiceFactory ( public Service getServce ) { return new ImplementationlO; )

9 Interfaces 591 ) class Implementationl implements Service { Implementation2 O {) //Acceso de paquete public void methodlI) (print("Implementation2 methodl);} public void method2 il (printi"Implementations method2 ");|

) class Implementation2Factory implements ServiceFactory ( public Service getService{) { return new Implementation2();

) i public class Factories { public static void serviceConsumer*ServiceFactory tact) ( Service s tact.getService(); a .methodl0 ) s.method2 ( ) ;

public static void main(String[J args) ( serviceConsumer(new ImplementationlFactory{)); // Las implementaciones son completamente intercambiables: serviceConsumer(new Implementation2Factory{)),

9 Interfaces 592 ) ) / * Output: Implementationl methodl Implementation! method2 Implementation methodl Implementation2 method2 *///:-

Sin cl mtodo factora, nuestro cdigo tendra que especificar en algn lugar el tipo exacto de objeto Service que se estuviera creando, para poder invocar el constructor apropiado.

Para qu sirve aadir este nivel adicional de indireccin? Una razn comn es para crear un marco de trabajo para el desarrollo. Suponga que estamos creando un sistema para juegos que permita, por ejemplo, jugar tanto al ajedrez como a las damas en un mismo tablero. //; interfaces/Games.java /'/ Un marco de trabajo para juegos utilizando mtodos factora, import static net .mindview.util.Print. *; interface Game ( boolean moveO; } inr^rfarp Gam<*Frt:nry { Gam* getGama(}* ) class Checkers implements Game ( private Int moves = 0 ; private static final int MOVES = 3; public boolean move(J ( print(Checkers move " moves); return -*~*-raoves 1= MOVES; 1 class Checkers Factory implements GaineFactory | public Game getGamei ) ( return new Checkers(); )

9 Interfaces 593 ) class Chess implements Game { private int moves = 0 ; private static final me MOVES ~ 4; public boolean moveU ( i print"Chess move " + moves-; return ^moves MOVES;

class ChessFactory Implements GameFactory ( public Game getGamed ( return new ChessO; )

) public class Games { public static void playGamelGameFactory Eactory) ( Game s factory.getGame0; while(s.move()) i

public static void main(Strmg(l args} ( playGame(new CheckersFactory()); playGame(new ChessFactory U);

) ) /* Output: Checkers move 0 Checkers move 1 Checkers move 2 Chess move 0 Chess move l Chess move 2 Chess move 3

9 Interfaces 594 Si la clase Games representa un fragmento complejo de cdigo, esta tcnica permite reutilizar dicho cdigo con diferentes tipos de juegos. Podemos fcilmente imaginar otros juegos ms elaborados que pudieran beneficiarse a la hora de desarrollar este diseo.

En el siguiente captulo, veremos una forma ms elegante de implemcntar las factoras utilizando clases internas annimas.

Ejercicio 18: (2) Cree una interfaz Cycle, con implemen tac iones Unleycle. Blcycle y Tricycle. Cree factoras para cada

tipo de Cycle y el cdigo necesario que utilicen estas factoras.

Ejercicio 19: (3) Cree un marco de trabajo utilizando mtodos factora que permita simular las operaciones de lanzar

9 Interfaces 595 una moneda y lanzar un dado. Resumen

Resulta bastante tentador concluir que las interfaces resultan tiles y que. por tanto, siempre son preferibles a las clases concretas. Por supuesto, casi siempre que creemos una clase, podemos crear en su lugar una interfaz y una factora.

Mucha gente ha cado en esta tentacin creando interfaces y factoras siempre que era posible. La lgica subyacente a este enfoque es que a lo mejor podemos necesitar en el futuro una implementacin diferente, por lo que aadimos siempre dicho nivel de abstraccin. Esta tcnica ha llegado a convertirse en una especie de optimizacin de diseo prematura.

La realidad es que todas las abstracciones deben estar motivadas por una necesidad real. Las interfaces deben ser algo que utilicemos cuando sea necesario para optimizar el cdigo, en lugar de incluir ese nivel adicional de indirecein en todas partes. ya que ello hace que aumente la complejidad. Esa complejidad adicional es significativa, y hacer que alguien trate de comprender ese cdigo tan complejo slo para descubrir al final que hemos aadido las interfaces por si acaso y sin una razn real, esa persona sospechara, con motivo, de todos los diseos que realicemos.

Una directriz apropiada es la que seala que las clases resultan preferibles a las interfaces. Comience con clases y, si esta claro que las interfaces son necesarias, redisee el cdigo. Las interfaces son una

9 Interfaces 596 herramienta muy conveniente, pero est bastante generalizada la tendencia a utilizarlas en demasa

Puede encontrar las solucione* a los ejercicios >eleccionadm en el documento electrnico Tlir Thttking InJaui AnrmtatedSolulUui Cmule, disponible para la venta en k-hu.Mim/View net.Clases internas

10

Resulta posible situar la definicin de una clase dentro de la definicin de otra. Dichas clases se llaman clases internas.

Las clases internas constituyen una caracterstica muy interesante, porque nos permite agrupar clases relacionadas y controlar la visibilidad mutua de esas clases. Sin embargo, es importante comprender que las clases internas son algo totalmente distinto al mecanismo de composicin del que ya hemos hablado.

A primera vista, las clases internas parecen un simple mecanismo de ocultacin de cdigo: colocamos las clases dentro de otras clases. Sin embargo, como veremos, las clases internas sirven para algo ms que eso: la clase interna conoce los detalles de la clase contenedora y puede comunicarse con ella. Asimismo, el tipo de cdigo que puede escribirse con las clases internas es ms elegante y claro (aunque no en todas las ocasiones, por supuesto)

Imcialmenie. las clases internas pueden parecer extraas y se requiere cierto tiempo para llegar a sentirse cmodo al utilizarlas en los diseos La necesidad de las clases internas no siempre resulta obvia, pero despus de describir la sintaxis bsica y la semntica de las clases internas, la seccin Para qu sirven las clases internas?* debera permitir que el lector se haga una idea de los beneficios de emplear este tipo de clases.

Despus de dicha seccin, el resto del captulo contiene un anlisis ms detallado de la sintaxis de las clases internas. Estas caractersticas se proporcionan con el tin de cubnr por completo el lenguaje, pero puede que no tengamos que usarlas nunca, o al menos no al principio. Asi pues, puede que el lector slo necesite consultar las partes iniciales del capitulo dejando los anlisis ms detallados como material de referencia Creacin de clases internas

Para crear una clase interna, el procedimiento que se utiliza es el que cabria suponer: la definicin de la clase se incluye dentro de otra clase contenedora: //: innerclasses/Parcel1 .j ava // Creacin de clases Internas. public class Parcel1 { class Contenes ( prvate iot i 11 ; public int valuIJ ( return i; |

) class Destination ( private String label;

Destination(String whereTo) ( label * whereTo;

) I String readLabelt) ( return label; )

// La utilizacin de clases internas se asemeja // a la de cualquier otra clase, dentro de Parcell:

public void ship(String dest) { Contenta c * new Contents(l; Destination d * new Destination(dest); System.out.println(d.readLabei());

) public static vod main(String[] args) { Parcell p * new Parcel1(); p.ship("Tasmania4");

) /* Output: Tasmania ///--

Las clases internas utilizadas dentro do ship( ) parecen clases normales. Aqui. la nica diferencia prctica es que los nombres estn anidados dentro de Parcell. Pronto veremos que esta diferencia no es la nica.

Lo ms normal es que una clase externa tenga un mtodo que devuelva una referencia a una clase interna, como puede verse en los mtodos to( ) y contents( ): //; innerclasses/Parcel2 .java // Devolucin de una referencia a una clase interna. public clase Parcei2 ( class Contents { prvate int i * 11 ; public int valu() { return i; }

i class Destination { private String label; Destination(String whereTo) { label = whereToj i String readLabei(> ( return label; ) J public Destination to(String s) { return new Destination(oI ;

) public Contents contentsO ( return new Contents();

) public void ship(String dest) { Contents c => contents(); Destination d to(dest); System.out.printIn(d.readLabeiO];

) public static void main(StringI1 args) ( Parcel2 p = new Parcel2 () , p.ship("Tasmania"); Parcel2 q = new Parcel2 i);

// Definicin de referencias a clases internas: Parcel2.Contents c = q.contentsO; Parcel2.Destination d q.to("Borneo");

) ) / Output: Tasmania

*///:-

Si queremos construir un objeto de la clase interna en cualquier lugar que no sea dentro de un mtodo no esttico de la clase extema, debemos especificar el upo de dicho objeto como NombreClaseExicnia.NotnbreClaschUcnta. como puede verse en tnain().

Ejercicio 1: (l) Escriba una clase denominada uter que contenga una clase interna llamada Inner. Aada un mto

do a Outer que devuelva un objeto de tipo Inner. F.n main ). cree e inicialice una referencia a un objeto taer. El enlace con la clase externa

Hasta ahora, parece que las clases internas son simplemente un esquema de organizacin de cdigo y de ocultacin de nombres. lo cual resulta til pero no especialmente necesario. Sin embargo, las cosas son ms complejas de lo que parecen, cuando se crea una clase interna, cada objeto de esa clase interna dispone de un enluce al objeto contenedor que o ha emulo. por lo cual puede acceder a los miembros de dicho objeto contenedor sin utilizar ninguna cualificacin especial. Adems, las clases internas tienen derechos de acceso a todos los elementos de la clase contenedora.20 Fl siguiente ejemplo ilustra esta caracterstica*. //: innerclasses/Sequence. ]ava / Almacena una secuencia de objetos. interface Selector ( fcoclean endl)? Object current()j void next();

) public class Sequence { prvate Object[J items; prvate int next = 0; public Sequence(int size) if(next < items.length) F.sto difiere significanvnmcnic del diseo de claves <imlaJa\ en 0+-*-. que simplemente se irnta de un mecanismo de ocultacin de nombres N:o hay ningn enlace ul objeto contenedor ni ningn tipo ile permisos implcitos en C-++.
20

items a new Object[aisej; }

public void ada{Object x,- {

tems[next++] = X;

} prvate class SequenceSelector implements Selector ( prvate int i. Q; public boclean endtl { return i == J.rem3 .length; ) public Object current(J ( return items[i]; ) J public void next O ( if(i< items.length) i*-*-; )

pablic Selector selecto*O ( return new SequenceSelector\); i public static void main(String11 args) ( Sequence 3 equence * new Sequence(10); ornt 1 = Oj i < i0; i-*-} sequence.add(Integer.toString(i)); Selector selector = sequence.selector) ; while(!selector.endO) { System.out.print (selector.current () + " *); selector.next();

) / Output: 0 1 2 3 4 5 6 7 9 9

*///:-

La secuencia Sequen ce es simplemente una matriz de tamao fijo de objetos Object con una clase envoltorio. Invocamos add( ) para aadir un nuevo objeto al t nal de la secuencia (si queda sitio). Para extraer cada uno de los objetos de la secuencia, hay una interfaz denominada Selector. ste es un ejemplo del patrn de diseo iterador del que hablaremos ms en detalle posteriormente en el libro. Un Selector permite ver si nos encontramos al final de la secuencia [end( )]. acceder al objeto actual (currenti )] y desplazarse al objeto siguiente (ne\t< )] de la secuencia. Como Selectores una interfaz, otras ciases pueden implementar la interfaz a su manera y otros mtodos pueden tomar la interfaz como argumento, para crear cdigo de propsito ms general.

Aqu, SequenceSeleetor es una clase privada que proporciona la funcionalidad Selector. En main(). podemos ver la creacin de una secuencia, seguida de la adicin de una serie de objetos de tipo String. A continuacin, se genera un objeto Selector con una llamada a selector! ), y este objeto se utiliza para desplazarse a travs de la secuencia y seleccionar cada elemento

A primera vista, la creacin de SequenceSeleetor se asemeja a la de cualquier otra clase interna. Pero examinemos el ejemplo en ms detalle. Observe que cada uno de los mtodos [end( ). current( ) y neit( )] hace referencia a Items, que es una referencia que no forma parte de SequenceSeleetor. sino que se encuentra en un campo privado dentro de la clase contenedora. Sin embargo, la clase interna puede acceder a los mtodos y campos de la clase contenedora como si fueran de su propiedad. lista caracterstica resulta muy cmoda, como puede verse en el ejemplo anterior.

Asi pues, una clase interna tiene acceso automtico a los miembros de la elase contenedora. Cmo

puede suceder esto? La clase interna captura en secreto una referencia al objete concreto de la clase contenedora que sea responsable de su creacin Entonces, cuando hacemos referencia a un miembro de la clase contenedora, dicha referencia se utiliza para seleccionar dicho miembro. Afortunadamente, el compilador se encarga de resolver todos estos detalles por nosotros, pero resulta evidente que slo podr crearse un objeto de la clase interna en asociacin con otro objeto de la clase contenedora (cuando, como veremos pronto, la clase interna sea no esttica). La construccin del objeto de la clase interna necesita de una referencia al objeto de la clase contenedora y el compilador se quejar si no puede acceder a dicha referencia. La mayor parte de las veces todo este mecanismo funciona sin que el programador tenga que intervenir para nada.

Ejercicio 2: (I) Cree una clase que almacene un objeto Strng y que disponga de un mtodo toStrinf( ) que muestre

esa cadena de caracteres. Aada varias instancias de la nuev a clase a un objeto Sequence y luego visualcelas.

Ejercicio 3: (l) Modifique el Ejercicio I para que Outer tenga un campo prvate String (inicializado por el construc

tor) e Inner tenga un mtodo toStrmgt ) que muestre este campo. Cree un objeto de tipo Inner y visualcelo

Utilizacin de .this y .new

Si necesita generar la referencia al objeto de la clase externa, basta con indicar el nombre de la clase extema seguido de mi punto y de la palabra clave tliis. La referencia resultante lendr automticamente el tipo correcto, que se conoce y se comprueba en tiempo de compilacin, por lo que no hay ningn gasto adicional en liempo de procesamiento. He aqu un ejemplo que muestra como utilizar .tliis: //: innerclasses/DotThis.java // Cualificacin del acceso al objeto de la clase externa. public class DotThis { void f() ( System.out.println("DotThis.f()*); ) public class Inner ( public DotThis outer() retum DotThis.this; // Un "this" hara referencia al "this" de Inner {

) public Inner inner() { return new Inner(); ) public static void main(String(] args) { DotThis dt = new DotThis():

i 10 Clases internas 608 DotThis. Inner dti = dt. inner 0:dti.cter O - f l) i } / OutpUt: DctThis.f <)

*///:-

Algunas veces, necesitamos decir a un objeto que cree otro objeto de una de sus clases internas. Para hacer esto es necesario proporcionar una referencia al objeto de la clase externa en la expresin new. utilizando la sintaxis .new. como en el siguiente ejemplo: //: innerclasses/DotNew.lava Creacin de una clase interna directamente utilizando la sintaxis .new. public clasa DotNew ( public class Inner () public static void main(Stxing[1 aras) { DotNew dn = new DotNew O; DotNew. Inner dni = dn.new Inner O;

i 10 Clases internas 609 } ///:-

Para crear un objeto de la clase interna directamente, no se utiliza esta misma forma haciendo referencia al nombre de la clase externa DotNew como cabria esperar, sino que en su lugar es necesario utilizar un objeto de la clase externa para crear un objeto de la clase interna, como podemos ver en el ejemplo anterior. Esto resuelve tambin las cuestiones relativas a los mbitos de los nombres en la clase interna, por lo que nunca escribiramos (porque, de hecho, no se puede) dn.new DotNew.Inner( ).

No es posible crear un objeto de la clase interna a menos que ya se disponga de un objeto de la clase externa. Esto .se debe a que el objeto de la clase interna se conecta de manera transparente al de la clase externa que lo haya creado. Sin embargo. si definimos una clase anidada, (una clase interna esttica), entonces no ser necesaria la referencia al objeto de la clase externa.

A continuacin puede ver cmo se aplicara .new al ciemplo Parcel**: //: nnerclasses/Parcel3.java // Utilizacin de .new para crear instancias de clases internas. public class Parcel3 ( class Contents ( prvate int i * 11 ; i public int valu O { return i; } class Destination ( prvate Strlng label; Destination(Strlng whereTo) { label = whereTo; ) String readLabel() { return label; }

i 10 Clases internas 610 ) public static void tnam String [] args) { Parcel3 p = new Parcel3(); // Hay que usar una instancia de la clase externa // para crear una instancia de la clase interna: Parcel3.Contents c = p.new ContentsO; Parcel3.Destination d = p.new Destination("Tasmama");

) ///:-

Ejercicio 4: (2)Aadaun mtodo a la clase Sequence.SequenceSelector que genere la referencia a la clase externa Sequence.

Ejercicio 5: (I) Cree una clase con una clase interna. En otra clase separada, cree una instancia de la clase interna

i 10 Clases internas 611 Clases internas y generalizacin

Las clases internas muestra su utilidad real cuando comenzamos a generalizar a una clase base y. en particular, a una interfaz. (L! efecto de generar una referencia a una interfaz a partir de un objeto que la implemente es prcticamente el mismo que el de realizar una generalizacin a una clase base). La razn es que entonces la clase interna (la implementacin de la interfaz) puede ser no visible y estar no disponible, lo cual resulta muy til para ocultar la implementacin. Lo nico que se obtiene es una referencia a la elase base o a la interfaz

Podemos crear interfaces para los ejemplos anteriores: //: nnerclasses/Destination.java public mterface Destination { String readLabelt); l ///://: nnerciasses/'Contents.java public interface Contents ( int valu()

} ///:-

i 10 Clases internas 612 Ahora Contents y Destination representan interfaces disponibles para el programador de clientes. Recuerde que una interfaz hace que todos sus miembros sean automticamente pblicos.

Cuando obtenemos una referencia a la clase base o a la interfaz, es posible que no podamos averiguar el tipo exacto, como se muestra en el siguiente ejemplo: //: innerclasses/TestParcel.java class Parcel4 { privare class PContents implements Contents { private int i = II; oubllc int valu(i { return i; }

) protected class FDestmation implements Destination ( private String label; private PDestinafcion(String whereTo ( label = whereTo;

) public String readLabelU { return label; }

i 10 Clases internas 613 ) public Destination destination(String s) ( return new PDestinationis); i public Contents contentsO { return new PContents();

} public class TestParcel ( public static void main(String[] args) { Parcel4 p = new Parcel4(); Contents c = p.contentsO; Destination d * p.destination("Tasmania"); // Ilegal -- no se puede acceder a la clase privada: l //! Parcel4 .PContents pe = p.new PContentsO;

) ///;-

i 10 Clases internas 614 En Parcel-* hemos aadido algo nuevo. La clase interna PContents es private, as que slo puede acceder a ella ParceM Las clases normales (no internas) no pueden ser privadas o protegidas: slo pueden tener acceso pblico o de paquete PDestination es protegida, por lo que slo pueden acceder a ella Parcel*!, las clases contenidas en el mismo paquete (>' que protected tambin proporciona acceso de paquete) y las clases que hereden de ParccM. Lsto quiere decir que el pro- gramador de clientes tiene un conocimiento de estos miembros y un acceso a los mismos restringido. De hecho, nu podemos ni siquiera realizar una esperializacin a una clase interna privada (ni a una clase interna protegida, a menos que estemos usando una clase que herede de ella), porque no se puede acceder al nombre, como podemos \er en ela&s XestParcel. Por tanto, las clases internas privadas proporcionan una forma para que los diseadores de clases eviten completamente las dependencias de la codificacin de tipos y oculten totalmente Ion detalles relativos a la implementacin. Adems, la extensin de una interfaz resulta intil desde la perspectiva del programador de clientes, ya que este no puede acceder a ningn mtodo adicional que no forme parte de la interfaz pblica. Este* tambin proporciona una oportunidad para que el compilador de Java genere codigo ms eficiente.

Ejercicio 6: (2)Cree una interfaz con al menos un mtodo, dentro de su propio paquete. Cree una clase en un paque

te separado. Aada una clase interna protegida que implcmcntc la interfaz. En un tercer paquete, defina una clase que herede de la anterior y. dentro de un mtodo, des uelva un objeto de la clase interna protegida. efectuando una generalizacin a la interfaz durante el retomo.

Ejercicio 7: (2) Cree una clase con un campo privado y un mtodo privado. Cree una clase interna con un mtodo que

i 10 Clases internas 615 modifique el campo de la clase externa e invoque el mtodo de la clase externa. En un segundo mtodo de la clase externa, cree un objeto de la clase interna e invoque su mtodo, mostrando a continuacin el efecto que esto tenga sobre el objeto de la clase externa.

Ejercicio 8: (2)Determine interna.

si una clase externa tiene acceso a los elementos privados de su clase

Clases internas en los mtodos y mbitos

Lo que hemos visto hasta ahora son los usos tpicos de las clases internas. En general, el cdigo que escribamos y el que podamos leer donde aparezcan clases internas estar compuesto por clases internas simples" que resulten fciles de comprender. Sin embargo, las sintaxis de las clases internas abarca varias otras tcnicas ms complejas. Las clases internas pueden crearse dentro de un mtodo o incluso dentro de un mbito arbitrario. Lxisten dos razones para hacer esto:

1.

Como hemos visto anteriormente, podemos estar implementando una interfaz de algn tipo para poder crear y devolver una referencia.

2.

Podemos estar tratando de resolver un problema complicado y queremos crear una clase que nos ayude a encontrar la solucin, pero sin que la clase est pblicamente disponible.

i 10 Clases internas 616 En los siguientes ejemplos, vamos a modificar el cdigo anterior para utilizar:

1.

Una clase definida dentro de un mtodo

2.

Una clase definida dentro de un mbito en el interior de un mtodo

3.

lina clase annima que implemento una interfaz

4.

Una clase annima que ample una clase que disponga de un constructor no predeterminado

5.

Una clase annima que se encargue de la inicializacin de campos

i 10 Clases internas 617


6.

Una clase annima que lleve a cabo la construccin utilizando el mecanismo de inicializacin de instancia (las clases internas annimas no pueden tener constructores).

El primer ejemplo muestra la creacin de una clase completa dentro del mbito de un mtodo (en lugar de dentro del mbito de otra clase), fisto se denomina clase interna /acal / f : innercla9 ses/Pareel5.j ava / / Anidamiento de una clase dentro de un mtodo. public claas Parcel5 ( public Destination destination(String s) { clase PDestination mplements Destination { prvate String label; private PDestinacion(Scring whereTo) ( label whereTo; ) public Strino readLabeliJ ( return label; }

) } return new PDestination(s);

public static void main (String [J args) { ParceLS p = new Pareis()/ Destination d * p.destlnation(TasmanlaM,,) ; 1 ) U Hr

i 10 Clases internas 618 a clase PDestination es parte de destina(inn( ) en lugar de ser parte de Parcel? Por tanto, no se puede acceder a PDestination fuera de destination( ). Observe la generalizacin que tiene lugar en la instruccin return: lo nico que sale de destination( ) es una referencia a Destination. que es la clase base. Por supuesto, el hecho de que el nombre de la clase PDestination se coloque dentro de destnation( ) no quiere decir que PDestination no sea un objeto vlido una vez que destination( ) termina.
1.

Podemos utilizar el identificador de clase PDestination para nombrar cada clase interna dentro de un mismo subdirectoro sin que se produzcan colisiones de clases.

1:1 siguiente ejemplo muestra cmo podemos anidar clases dentro de un mbito arbitrario. //: nnerclasses/Parcele.java // Anidamiento de una clase dentro de un mbito. public claBS Parcel ( prvate void intemalTrackinglbooiean b) ( if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id ** S;

} String getSlipO { return id; )

i 10 Clases internas 619 ) TrackingSlip ts = new TrackingSlip("slip") ; String s = ts.getSlipO ; i // |No se puede usar aqu! Fuera de mbito: //1 TrackingSlip ts = new TrackingSliD i."x" I ;

) public void trackO | intemalTracking( true) ; ) public static void mainStTingn argsl ( Parcel p = new ParcelGO; p.track(};

} ///*-

La clase TrackingSlip est anidada dentro del mbito de una instruccin if. Esto no quiere decir que la case se cree condicionalmente; esa clase se compila con todo el resto del cdigo. Sin embargo, la clase

i 10 Clases internas 620 no est disponible fuera del mbito en que est definida. Por lo dems, se asemeja a una clase normal.

Ejercicio 9: (I) Cree una interfaz con al menos un mtodo e implemente dicha interfaz definiendo una clase interna

dentro de un mtodo que devuelva una referencia a la interfaz.

Ejercicio 10: (l) Repita el ejercicio anterior, pero definiendo la clase interna dentro de un mbito en el interior de un

mtodo.

Ejercicio 11: (2) Cree una clase interna privada que implemente una interfaz pblica. Escriba un mtodo que devuelva

i 10 Clases internas 621 una referencia a una instancia de la clase interna privada, generalizada a la interfaz. Demuestre que la clase interna est completamente oculta, tratando de realizar una especializacin sobre la misma. Clases internas annimas

El siguiente ejemplo puede parecer un tanto extrao: //: innerclassea/Parcel7.java // Devolucin de una instancia de una ciase interna annima. public class Parcel? ( public Contenta contenta O { retum new Contentad { // Inserte una definicin de clase prvate int = 11 ,* public int valu O { retum i; } ); // En este caso hace falta el punto y coma

} public static void main{String() aras) { Parcel7 p = new Parcel7(); Contente c = p.contentaJ ; ) } ///-.-

El mtodo contents() combina la creacin del valor de retomo con la definicin de la clase que

i 10 Clases internas 622 representa dicho valor de retomo. Adems, la clase es annima, es decir, no tiene nombre. Para complicar an ms las cosas, parece como si estuviramos empezando a crear un objeto Contents, y que entonces, antes de llegar al punto y coma, dijramos Un momento: voy a introducir una definicin de clase".

Lo que esta extraa sintaxis significa es: "Crea un objeto de una clase annima que herede de Contents" La referencia devuelta por la expresin new se generalizar automticamente a una referencia de tipo Contents. La sintaxis de la clase interna annima es una abreviatura de: //: innerclasses/Parcel7b.java // Versin expandida de Parcel7.java public class ParceI7b { class MyContents implements Contents 1 private int i * XI ublic nt valu0 { return i; )

) public Contents contents() { retum new MyContentsO; ) public static void main(Stringfl args1 { ParceI7b p = new Parcei7b(); Contents c = p.contental) ;

i 10 Clases internas 623 1 ///:-

En la clase interna annima. Contents se crea utilizando un constructor predeterminado.

El siguiente cdigo muestra lo que hay que hacer si la clase base necesita un constructor con un argumento: /./; mnerclasses/Pareis . java // Invocacin del constructor de la clase base. public ciass Parcela ( public Wrapping wrapping(int x) ( // Llamada al constructor de la clase base: retum new Wrapping(x) ( constructor, public int valu(I ( retum super.valu() 47; //Pasarargumento del

) ) t // Punto y coma necesario

i 10 Clases internas 624 i

625 Piensa en Java public static void malntStrlngll aras) (Parcela p = new Parcele(); Wrapping w = p.wrapping(10);

) J

Es decir, simplemente pasamos c! argumento apropiado aJ constructor de la clase base, como sucede aqui con la x que se pasa en new \N rapping(x). Aunque se trata de una clase normal con una implementacin. Wrapping se est usando tum. bien como interfaz" con sus clases derivadas: //: innerciasses/Wrappina.java public class Wrapping { private int i? public Wrapping(int x) ( i = x; } public int valu() ( return i; )

) ///:-

626 Piensa en Java Como puede observar. W rapping tiene un constructor que requiere un argumento, para que las cosas sear un poco ms interesantes.

El punto y coma situado al final de la clase interna annima no marca el final del cuerpo de la clase, sino el final de la expresin que contenga a la clase annima. Por tanto, es una utilizacin idntica al uso del punto y coma en cualquier otro lugar

Tambin se puede realizar la tnicializacin cuando se definen los campos en una clase annima: //: nnerclasses/Parcel9.java // Una clase interna annima que realiza la // inicialzacin. Versin ms breve de Pareis.java. public class Parcel9 ( // El argumento debe ser final para poder utilizarlo // dentro de la clase interna annima: public Destination destination(final String dest) ( return new Destination(J { private String label * dest; public String readLabelO ( return label; )

627 Piensa en Java };

) public static void main(StringLl args) { Parcel9 p * new Farcel9(); i Destination d * p.destination("Tasmania");

} ///:-

Si estamos definiendo una clase interna annima y queremos usar un objeto que est definido fuera de la clase interna annima. el compilador requiere que la referencia al argumento sea final, como puede verse en el argumento de dcstination( ) Si nos olvidamos de hacer esto, obtendremos un mensaje de error en tiempo de compilacin.

Mientras que estemos simplemente realizando una asignacin a un campo, la tcnica i

628 Piensa en Java empleada en este ejemplo resulta adecuada. Pero que sucede si necesitamos realizar algn tipo de actividad similar a la de los constructores? No podemos disponer de un constructor nominado dentro de una clase annima (ya que la clase no tiene ningn nombre), pero con el mecanismo de micializacin Je instancia, podemos, en la practica, crear un constructor para una clase interna annima, de la forma siguiente: f f : Innerclasses/AnonymousConstructor.java // Creacin de un constructor para una clase interna annima, import static net .mindvew.til. Print. abstract class Base { public Base(int i) { printC'Base constructor, i = * -* i)

) public abstract void fO ;public class AnonymousConstructor ( public static Base getBase(int i) ( retum new Base (i) ( ( print {Inside mstance initializer"); } public void () {

printCIn anonymous E O " >;

I ;

629 Piensa en Java ) public static void main(String(] args) { Base base = gesBase( 47); base.f( ) i

) J / Output: Base constructor, i 47 iTiside ir.stanee initalizer Xa anonymous f (I ///:-

En es le caso, la variable i no tenia porqu haber sido final Aunque se pasa i al constructor base de la clase annima, nunca se utili/a esa variable dentro de la clase annima.

He aqu un ejemplo con inicializacin de instancia. Observe que los argumentos de destinationt ) deben ser de tipo final, puesto que se los usa dentro de la clase annima: i //: mnerclasses/ParcellO. java

630 Piensa en Java // Uso de Minicializacn de instancia" para realizar // la construccin de una clase interna annima. public class ParcellO ( public Destination destinacin(final String dest, final float pricej { return new Destination!) { prvate int cost;
II

Inicializacin de instancia para cada objeto:

{ cost a Math.round (price); if(cost > 100 ) I Systen;.out. println(MOver budget tM) ;

prvate String label - dest: public String readLabelO { retum label; )

) public static void mair I String [] args) ( ParcellO p * new ParcellOO; Destination 101.395F); i d ^ p. destination ("Tasmania'*,

631 Piensa en Java > } / Output: Over budgetI ///:-

Dentro del micializador de instancia, podemos ver cdigo que no podra ejecutarse como parte de un micializador de campo (es decir, la instruccin if). Por tanto, en la prctica, un micializador de instancia es el constructor de una clase interna annima. Por supuesto, esta solucin est limitada: no se pueden sobrecargar los micializadores de instancia, asi que slo podemos disponer de uno de estos constructores. Las clases internas annimas estn en cierta medida limitadas si las comparamos con el mecanismo normal de herencia, porque tienen que extender una clase o implementai una interfaz, pero no pueden hacer ambas cosas al mismo tiempo. Y. si implementamos una interfaz, slo podemos implcmentar una

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 632 Piensa en Java .Ejercicio 13: (!) Repita el Ejercicio 9 utilizando una clase interna annima

Ejercicio 14: interfaces/HorrorShow.ja\ a para implementar utilizando

d) Modifique Dmngerous.Monster

Varapire

clases annimas.

Ejercicio 15: predeterminado (uno

t2) Cree unaclase con un constructor no que tengaargumentos) y sin ningncons

tructor predeterminado (es decir, un constructor sin argumentos). Cree una segunda clase que tenga un mtodo que devuelva una referencia a un objeto de la primera clase. Cree el objeto que hay que devolver definiendo una clase interna annima que herede de la primera clase.

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 633 Piensa en Java Un nuevo anlisis del mtodo factora

Observe que el ejemplo interfaees/factories.java es mucho ms atractivo cuando se utilizan clases internos annimas //: innerclasses/Factories.java tnport stat;c net . mmdview. til .Print. * ; interface Service { void methodl O / void method( i ; ) nterface ServiceFactory ( Service qetService< ) \ class Implementationl implements Service { prvate Implementationl() {) public void methodl() {printf"Implementationl methodl"! i) public void method2C' {print("Implementationl method2") ,) public static ServiceFactory factory = new ServiceFactory() ( public Service getServiceO ( return new Implementationl () ;

class Implementation2 implements Service { private Implementation2() {} public void methodlf {print("Implementation methodl";) public void method2() (print^Implementations methods");) public static ServiceFactory factory = new ServiceFactory() ( public Service getServiceO ( return new Implementations(;

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 634 Piensa en Java )

} public class Factories { public static void serviceConsumeriServiceFactory fact) ( Service s fact.getServica0; 3 .methodl ()/ s.method2 0;

) public static void main(StringM args1 ( serviceConsumer(Implementationl.factor y); // Las mplementaciones son completamente intercambiables: servceConsumer(Implementation2.factory)j

) / Output: Implementationl methodl Implementation! metho2 Implementation^ methodl

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 635 Piensa en Java jmplementatlon2 method2 ///:-

Ahora, los constructores de Implementation I e Implementation! pueden ser privados y no hay ninguna necesidad tie crear una clase nominada como factora. Adems, a menudo slo hace falta un nico objeto factora, de modo que aqu lo hemos creado como un campo esttico en la implementacin de Service. Asimismo, la sintaxis resultante es ms clara. Tambin podemos mejorar el ejemplo Interfaces/Gam ei.ja va utilizando clases internas annimas: //: nnerclasses/Games.java Utilizacin de clases internas annimas con el marco de trabajo Game, import static net.mindview.util.Print interface Game ( boolean move(); } interface GameFactory | Game getGameO; ) class Checkers implements Game ( private Checkers() () private int moves * 0 ; private static final int MOVES 3; public boolean movep ( print ("Checkers move " 4 - moves); return amoves 1= MOVES;

) public static GameFactory factory * new GameFactory() ) public Game getGameO ( return new Checkers(); ) I class Che35 implements Game { private Chess 0 () private int moves = 0 ; private static final int MOVES * 4; public boolean move() (

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 636 Piensa en Java print "Chess move " -f moves) ; return moves 1 * MOVES;

public static GameFactory factory = new GameFactory() ( public Game getGameO I return new ChessO; )

!;

) public class Games ( public static void playGame(GameFactory factory) ( Game s = factory .getGame(l; while(s.move <)) J

public static void mainiString(J args) ( playGame'Checkers.factory !; playGameiChess.factory);

) ) / Output: Checkers move Checkers move Checkers 0 I

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 637 Piensa en Java move 2 Chess move 0 Chess move 1 Cheas move 2 Chess move 3 *///:-

Recuerde el consejo que hemos dado al final del ltimo captulo: Utilice las clases con preferencia a las interfaces. Si su diseo necesita una interfaz, ya se dar cuenta de ello. En caso contrario, no emplee una interfaz a menos que se vea obligado.

Ejercicio 16: (l) Modifique la solucin del Ejercicio 18 del Capitulo 9. Interfaces para utilizar clases internas annimas

Ejercicio 17: (1) Modifique la solucin del Ejercicio 19 del Captulo 9. Interfaces para utilizar clases internas annimas

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 638 Piensa en Java Clases anidadas

Si no es necesario disponer de una conexin entre el objeto de la clase interna y el objeto de la clase externa, podemos definir la clase interna como esttica. Esto es lo que comnmente se denomina una clase anulada.21 Para comprender el significado de la palabra clave static cuando se la aplica a las clases internas, hay que recordar que el objeto de una clase interna normal mantiene implcitamente una referencia al objeto de la clase contenedora que lo ha creado. Sin embargo, esto no es cierto cuando definimos una clase interna como esttica. Por tanto, una clase anulada significa:

1.

Que no es necesario un objeto de la clase externa para crear un objeto de la clase anidada.

Que no se puede acceder a un objeto no esttico de la clase externa desde un objeto de una clase anidada.
2.

Las clases anidadas difieren de las clases internas ordinarias tambin en otro aspecto. Los campos y los mtodos en las clases internas normales slo pueden encontrarse en el nivel extemo de una clase, por lo que las clases internas normales no pueden tener datos estticos, campos estticos o clases anidadas. Sin embargo, las clases anidadas pueden tener cualquiera de estos elementos. //: innerclasses/Parcelll. java // Clases anidadas (clases internas estticas). < manta cierto purccido con las clases anidadas de Cn salvo porque dichas clases no permiten acceder a miembros privado a diferencia de lo que succ de en Java.
21

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 639 Piensa en Java public class Parceill ( prvate static class PaxcelContents lmplements Contents ( privare iut i = 11 / public int valu(J { retum i; }

) protected static clasB ParcelDestinatiem implements Destination ( prvate Stnng labei; private ParcelDestinationiString whereTo) { labei - whereTo,*

> public String readLabelf) ( return labei; ) // Las clases anidadas pueden contener otros elementos estticos: public static void f() (} static int x = 10 ; static class AnotherLevel { public static void f() () static int x = 10 ; i

} public static Destination destination(String s) ( retum new ParcelDestination(s) ; i public static Contents contents/) ( retum new ParcelContents{)j

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 640 Piensa en Java 1 public static void main<Stringl] args; { Contents c = contents); Destination d * destination("Tasmania" ) ;

) J ///:-

En main( I. no es necesario ningn objeto de Parcel 11. en su lugar, utilizamos la sintaxis normal para seleccionar un miembro esttico con el que invocar los mtodos que devuelven referencias a Contents y Destination

Como hemos visto anteriormente en el capitulo, en una clase interna normal (no esttica), el vinculo con el objeto de clase externa se utiliza empleando una referencia this especial. Una clase anidada no tiene referencia this especial, lo que hace que sea anloga a un mtodo esttico

Ejercicio 18: (I) Cree una clase que contenga una clase anidada. En main( ). cree una instancia de la clase anidada.

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 641 Piensa en Java Ejercicio 19: (2) Cree uua clase que contenga una clase interna que a su vez contenga otra clase interna. Repita el pro

ceso utilizando clases anidadas. Observe los nombres de los archivos .class generados por el compilador. Clases dentro de interfaces

Normalmente, ni' podemos incluir cualquier cdigo dentro de lina interfaz, pero una clase anidada puede ser parte de una interfaz Cualquier clase que coloquemos dentro de una interfaz ser automticamente pblica y esttica. Puesto que la clase es esttica no viola las reglas de las interfaces: simplemente, la clase anidada se incluye dentro del espacio de nombres de la interfaz Podemos incluso implementar la interfaz contenedora dentro de la clase contenedora de la forma siguiente, como por ejemplo en: //: mnerciasses/ClassInlnterface .java // (mairi: Classlnlnterface$Test) public interface Classlnlnterface ( void howdy (); ciass Test impiemente Classlnlnterface { ptiblic void howrly I) { System.out .prmtln ("Howdy 1 " I;

) public static void mam (String U args) { new Test().howdy();

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 642 Piensa en Java ) i } *Output: Howd yl ///:-

Resulta bastante til anidar una clase dentro de una interfaz cuando queremos crear un cdigo comn que haya que emplear con todas las diferentes mplementaciones de dicha interfaz.

Anteriormente en el libro ya sugeramos incluir un mtodo main( ) en todas las clases, con el fin de utilizarlo como mecanismo de prueba de estas clases. Una desventaja de esta tcnica es la cantidad de cdigo compilado adicional con la que hay que trabajar. Si esto representa un problema, pruebe a utilizar una clase anidada para incluir el cdigo de prueba: //: mnerclasseB/TestEed.java // Inclusin del cdigo de prueba en una clase anidada. // {tttain: TestBedSTester} public ciass TeatBed ( public void f() { Sy8 tein.ot.prntlnl "f i i " l ; ) public static ciass Tester {

public static void maltt(String J args | TestBed c new TescBedli/ t . f O f

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 643 Piensa en Java )

) ) / Output: f O *///*-

Esto genera una clase separada denominada TestBedSTettcr (para ejecutar c! programa, escribiramos java TestBedSTester). Puede utilizar esta clase para las pniebas. pero no necesitara incluirla en el producto final, bastar con borrar TcxtBedSTestcr.class antes de crear el producto definitivo.

Ejercicio 20: (1| Cree una interfaz que contenga una clase anidada. Implemento esta interfaz y cree una instancia de la

clase anidada.

Ejercicio 21: (2) Cree una interfaz que contenga una clase anidada en la que haya un mtodo esttico

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 644 Piensa en Java que invoque los

mtodos de la interfaz y muestre los resultados. Implemente la interfaz y pase al mtodo una instancia de la implementacin. Acceso al exterior desde una clase mltiplemente anidada

No importa con qu profundidad pueda estar anidada una clase interna: la clase anidada podr acceder transparentemente a todos los miembros de todas la clases dentro de las cuales est anidada, como podemos ver en el siguiente ejemplo:* //: Innerclasses/MultiNestingAccess. java // Las clases anidadas pueden acceder a todas los miembros de // todos los niveles de las clases en las que est anidada. ciass MNA { private void f() {} ciass A j private void gO () public ciass B { void hl) (

g t ) ; f t *

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 645 Piensa en Java )

) public ciass MultiNestingAccess ( public static void main(Stringl args) ( MNA mna * new MNA () j MNA. A tnnaa = mna. new A () ; ) ) ///:MNA.A.B mnaab = mnaa.new B(); mnaab.h( > ;

Puede ser que en MNA.A.B. los mtodos j() y f() son invocables sin necesidad de ninguna cualificacin (a pesar del hecho de que son privados) Este ejemplo tambin ilustra la sintaxis necesaria para crear objetos de clases internas mltiplemente anidadas cuando se crean los objetos en una clase diferente. La sintaxis ".new" genera el mbito correcto, por lo que no hace falta cualificar el nombre de la clase dentro de la llamada al constructor.

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 646 Piensa en Java iracia* iic mievu u Martin Dannci Para qu se usan las clases internas?

Hasta este momento, hemos analizado buena parte de los detalles sintcticos y semnticos que describen la forma de funcionar de las clases internas, pero esto no responde a la pregunta de para qu sirven las clases internas. Por qu los diseadores de Java se tomaron tantas molestias para aadir esta caracterstica fundamental al lenguaje?

Normalmente, la clase interna hereda de otra clase o implemento una interfaz, y el cdigo incluido en la clase interna manipula el objeto de la clase externa dentro del cual hubiera sido creado. Asi pues, podramos decir que una clase interna proporciona una especie de ventana hacia la clase externa.

Una de las cuestiones fundamentales acerca de las clases internas es la siguiente: si simplemente necesitamos una referencia a una interfaz, por qu no hacemos simplemente que la clase extema implemente dicha interfaz? La respuesta es que: *Si eso es todo lo que necesita, entonces esa es la manera de hacerlo". Por tanto, qu es lo que distingue una clase interna que unplementa una interfaz de una clase externa que implementa la misma interfaz? La respuesta es que no siempre dis ponemos de la posibilidad de trabajar con interfaces, sino que en ocasiones nos vemos forzados a trabajar con implcmenta- ciones Por tanto, la razn ms evidente para utilizar clases internas es la siguiente: Cada dase interna puede heredar de una imp/ementaein de manera independiente Por tanto, la dase interna no est limitada por d hecho de si la dase externa ya est heredando de una implementarin

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 647 Piensa en Java Sin la capacidad de las clases internas para heredar, en la prctica, de ms de una clase concreta o abstracta, algunos problemas de diseo y de programacin seran intratables. Por tanto, una forma de contemplar las clases internas es decir que representan el resto de la solucin del problema de la herencia mltiple. Las interfaces resuelven parte del problema, pero las clases internas permiten en la prctica una herencia de mltiples mplementaciones** ln otras palabras, las clases internas nos permiten en la prctica heredar de vanos elementos que no sean interfaces

Para analizar esto con mayor detalle, piense en una situacin en la que tuviramos dos interfaces que deban de alguna forma ser implementadas dentro de una clase. Debido a la flexibilidad de las interfaces, tenemos dos opciones una nica clase o una clase interna.

//: mnerclasses/Multilnterfaces.java

// Dos formas de impiemintar mltiples interfaces con una clase, package lnnerclasses; interface A {) lterface B (} clase X implementa A. B (} class Y Lmplements A {

B raakeB\J (

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 648 Piensa en Java // Clase interna annima: retum new B <) {}; ii

public ciass Multilntertaces ( static void takesAlA a) {) 3tatic void takesB{B b) {} public stacic void main{String[] argsj j X x * new X i) Y y = new () ; takesAx) ; takesA y>; takesB *x); takesB(y.makeB i));

) )//I-

Por supuesto, esto presupone que la estructura del cdigo tenga sentido en ambos casos desde el pumo de vista lgico. Sin embargo, normalmente dispondremos de algn tipo de directriz, extrada de la propia naturaleza del problema, que nos indicar M debemos utilizar una nica clase o una clase interna, pero en ausencia de cualquier otra restriccin, la tcnica utilizada en el ejemplo anterior no presenta muchas diferencias desde el punto de vista de la implementacin. Ambas soluciones funcionan adecuadamente.

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 649 Piensa en Java Sin embargo, si tenemos clases abstractas o concretas en lugar de interfaces, nos veremos obligados a utilizar clases internas si nuestra clase debe implemeniar de alguna forma las otras clases de las que se quiere heredar: //: lnnerclasses/Multilmplementation.java // Con clases abstractas o concretas, las clases // internas son la nica forma de producir el efecto // de la "herencia de mltiples impiementaciones* packaae innerclasses; clasB D () abstract ciass E () ciass Z excends D { E makeEil ( return new E{) j); } 1 public ciass Multilmpleraentation ( static void takesDD d) () 3tatic void takesE( e) () public static void maintStringII argsl ( Z 2 new Z (J f takesD<z); takesE\2.make E()) j

) J ///*-

Si no necesitramos resolver el problema de la herencia de mltiples iinplemcntaciones". podramos escribir el resto del programa sin necesidad de utilizar clases internas. Pero con las clases internas tenemos, adems, las siguientes caractersticas adicionales:

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 650 Piensa en Java
1.

La clase interna puede tener mltiples instancias, cada una con su propia informacin de estado que es independiente de la informacin contenida en el objeto de la clase externa.

2.

En una nica clase externa, podemos tener vanas clases internas, cada una de las cuales implementa la misma interfaz o hereda de la misma clase en una forma diferente En breve mostraremos un ejemplo de este caso.

El punto de creacin del objeto de lu clase interna no est ligado a la creacin del objeto de la clase externa
3.

4.

No existe ninguna relacin de tipo es-un* potencialmente confusa con la clase interna, se trata de una entidad separada.

Por ejemplo, si Sequen ce. ja va no utilizara clases internas, estaramos obligados a decir que un objeto Scquence es un objeto Selector, y slo podra existir un objeto Selector para cada objeto Sequence concreto. Sin embargo, podramos fcilmente pensar en definir un segundo mtodo. re\erseSelector( ). que produjera un objeto Selector que se desplazara en sentido inverso a travos de la secuencia. Este tipo de flexibilidad slo est disponible con las clases internas. Ejercicio 22: (2) Implemento reverseSelector( ) en Sequence. ja va.

Ejercicio 12:< I )Repita elEjercicio 7 utilizando una clase interna annima. 651 Piensa en Java Ejercicio 23: (4) Cree una interfaz l con tres mtodos. Cree una clase A con un mtodo que genero una referencia a l

10 Clases Internas 652 definiendo una clase interna annima. Cree una segunda clase B que contenga una matriz de V. B debo tener un mtodo que acepte y almacene una referencia a U en la matriz, un segundo mtodo que configure una referencia en la matriz (especificada mediante el argumento del mtodo) con el valor nuil, y un tercer mtodo que se desplace a travs de la matriz e invoque los mtodos de l. En main( ), cree un grupo de objetos A y un nico B Rellene el objeto B con referencias a U generadas por los objetos A. Utilice el objeto B para realizar llamadas a todos los objetos A Elimine algunas de las referencias U del objeto BCierres y retrollamada

Un cierre (closure) es un objeto invocable que retiene informacin acerca del mbito en que fue creado. Teniendo en cuenta esta definicin, podemos ver que una clase interna es un cierre orientado a objetos, porque no contiene simplemente cada elemento de informacin del objeto de la clase externa **el mbito en que fue creado), sino que almacena de manera automtica una referencia que apunta al propio objeto de la clase externa, en el cual tiene permiso para manipular todos los miembros, incluso aunque sea privados.

Uno de los argumentos mas slidos que se proporcionaron para incluir algn mecanismo de punteros en Java era el de permitir las retrollamudasi (caflbacks), Con una retrollamada. se proporciona a algn otro objeto un elemento de informacin que le permite llamar al objeto original en un momento posterior. Se irata de un concepto muy potente, como veremos ms adelante. Sin embargo, si se implementa una retrollamada utilizando un puntero, nos veremos forzados a confiar en que el programador se comporte correctamente y no haga un mal uso del puntero. Como hemos visto hasta el momento, el lenguaje Java tiende a ser bastante ms precavido, por lo que no se han incluido punteros en el lenguaje.

10 Clases Internas 653 El cierre proporcionado por la clase interna es una buena solucin, bastante ms flexible y segura que la basada en punteros. Veamos un ejemplo: //; innerclasses/CalIbacks.java / / Utilizacin de clases internas para las retrollamadas package innerclasses/ import static net.mindview.til.Print; nterface Incrementable ( void increment(J;

) // Muy eimple para limitarse a implementar la interfaz: ciass Calieel implements Incrementable ( private int i * 0 ; public void increment() { i-H- print(i);

i i ciass Mylncrement ( public void incremento { printl1MOther operatlon"); ) scatic void f(Mylncrement mi) { mi. increment; O ; }

10 Clases Internas 654 I // Si nuestra clase debe implementar incremento de // alguna otra forma, es necsario utilizar una clase interna: ciass Callee2 extends Mylncrement j private int i = 0 ; public void increment <) ( super.increment t} ; 1-M-; printII); i private ciass Closure implements Incrementable { public void increment( ) ( // Especifique un mtodo de la clase externa; en caso // contrario, se producirla una recursin infinita: i Callee2.this.increment();

} Incrementable aetCallbacfcReferenceO { return new Closure( )

) clas9 Caller ( prvate Incrementable callbackHeference; CallerIncrementable cbhl i

10 Clases Internas 655 ( callbackReference * cbh,- ] void ao() { calIbackP.eference.mcrement O ; ]

) public class Callbacks { public 3 tatic void main(StringC] args) { Callee! el new CalleelO; Callee2 c2 = new Callee2(); Mylttcrement.f(c2 ) 7 Caller callerl * new Caller(cll; Caller caller2 * new Caller(c2.getCallbackReferenceO); callerl.go ); callerl.go(); caller2 .go< J; caller2 .go(); i ) / Output t Other operation 1

12 Other operation 2 Other operation 3

10 Clases Internas 656 *///:-

Esto muestra tambin una distincin adiciona! entre el hecho de implementar una interfaz en una clase externa y el hecho de hacerlo en una clase interna. CalleeI es claramente la solucin ms simple en trminos de cdigo. C allee2 hereda de Mylncrement. que ya dispone de un mtodo lncrement( ) diferente que lleva a cabo alguna tarea que no est relacionada con la que la interfaz Incremcntahle espera. Cuando se hereda M\ Increment en Callee2. incrcment( ) no puede ser sustituido para que lo utilice Incrementable, por lo que estamos obligados a proporcionar una implementacin separada mediante una clase interna Observe tambin que cuando se crea una clase interna no se aade nada a la interfaz de la clase externa ni se la modifica de ninguna manera.

Todo en Callee2 es privado salvo getCalIbackRecrcnce ). Para permitir algn tipo de conexin con el mundo exterior, la interfaz Incremcntable resulta esencia!. Con este ejemplo podemos ver que las interfaces permiten una completa separacin entre la interfaz y la implementacin.

La clase interna Closure implementa Incremcntable para proporcionar un engarce con Callee2, pero un engarce que sea

10 Clases Internas 657 lo suficientemente seguro. Cualquiera que obtenga la referencia a Incrementable slo puede por supuesto invocar incre- ment() y no tiene a su disposicin ninguna otra posibilidad (a diferencia de un puntero, que nos permitira hacer cualquier

cosa).

Caller loma una referencia . Incrementable en su constructor (aunque la captura de la referencia de rctrollamada podra tener lugar en cualquier instante) y luego en algn momento posterior utiliza la referencia para efectuar una rctrollamada a la clase Callee

El valor de las retrollamadas radica en su flexibilidad; podemos decidir de manera dinmica qu mtodos van a ser invocado en tiempo de ejecucin Las ventajas de esta manera de proceder resultarn evidentes en el Capitulo 22, Interfaces grficas Je usuario, en el que emplearemos de manera intensiva las retrollamadas para implementar la funcionalidad GUI {Graphtcal User Inter/ace).

10 Clases Internas 658 Clases internas y marcos de control

Un ejemplo ms concreto del uso de clases internas es el que puede encontrarse en lo que denominamos matxu Je conttvl

Un mareo de trabajo de una aplicacin es una clase o un conjunto de clases diseado para resolver un tipo concreto de problema. Para aplicar un marco de trabajo de una aplicacin, lo que normalmente se hace es heredar de una o ms clases y sustituir algunos de los mtodos, ti cdigo que escribamos en los mtodos sustituidos sirve para personalizar la solucin general proporcionada por dicho marco de trabajo de la aplicacin, con el fin de resolver nuestros problemas especficos. Se trata de un ejemplo del patrn de diserto basado en el mtodo de plantillas {vase Thinking in Patterns twith Ja\a) en www.\tindllew.nci\ ti mtodo basado en plantillas contiene la estructura bsica del algoritmo, e invoca uno o ms mtodos sustituibles con el fin de completar la accin que el algoritmo dictamina. Los patrones de diseo separan las cosas que no cambian de las cosas que si que sufren modificacin y en este caso el mtodo basado en plantillas es la parte que permanece invariable, mientras que los mtodos sustituibles son los elementos que se modifican

Un marco de control es un tipo particular de marco de trabajo de aplicacin, que est dominado por la necesidad de responder a cierto suceso Los sistemas que se dedican principalmente a responder a sucesos se denominan sistemas dirigidos por sucesos. Un problema bastante comn en la programacin de aplicaciones es la interfaz grfica de usuario (GUI), que est casi completamente dirigida por sucesos. Como veremos en el Capitulo 22. Interfaces grficas de usuario, la biblioteca Swing de Java es un marco de i

10 Clases Internas 659 control que resuelve de manera elegante el problema de las interfaces GUI y que utiliza de manera intensiva las clases internas.

Para ver la forma en que las clases internas permiten crear y utilizar de manera sencilla marcos de control, considere un marco de control cuyo trabajo consista en ejecutar sucesos cada vez que dichos sucesos estn "listos. Aunque listos podra significar cualquier cosa, en este caso nos basaremos en el dato de lu hora actual. El ejemplo que sigue es un marco de control que no contiene ninguna informacin especifica acerca de qu es aquello que est controlando. Dicha informacin se suministra mediante el mecanismo de herencia, cuando se implementa la parte del algoritmo correspondiente al mtodo actionf ).

En primer lugar, he aqu la interfaz que describe los sucesos de control. Se trata de una clase abstracta, en lugar de una verdadera interfaz, porque el comportamiento predeterminado consiste en llevar a cabo el control dependiendo del instante actual. Por tanto, parte de la implementacin se incluye aqu: I I j innerclasses/controller/Bvent.java Los mtodos comunes para cualquier suceso de control, package innerclasses.controller;
II

public abstract ciass Event { private long eventTime; protected final long deiayTime; public Event(long deiayTime) ( this.deiayTime = deiayTime; atart t)/

10 Clases Internas 660 ) public void start) ( // Permite la reinicializacin eventTime System.nanoTlme(l deiayTime; public boolean readyO ( rettirn System.nanoTimeO eventTime;

) public abstract void acticnO;

) ///:-

El constructor captura el tiempo (medido desde el instante de creacin del objeto) cuando se quiere ejecutar el objeto Event. v luego invoca start( ). que toma el instante actual y aade el retardo necesario, con el fin de generar el instante en el que el suceso tendr lugar F.n lugar de incluirlo en el constructor. sturt( ) es un mtodo independiente De esta forma, se puede rcinicializarel temponzador despus de que el suceso haya caducado, de manera que el objeto Event puede reutilizarse. Por ejemplo, si queremos un suceso repetitivo, podemos invocar simplemente start() dentro del mtodo action()

10 Clases Internas 661 n?ady() nos dice cundo es el momento de ejecutar el mtodo action( ) Por supuesto. rcady( ) puede ser sustituido en una clase derivada, con el fin de basar el suceso Event en alguna otra cosa distinta del tiempo.

El siguiente archivo contiene el marco de control concreto que gestiona y dispara los sucesos Los objetos Event se almacenan dentro de un objeto contenedor de tipo Uit< Event > (una lista de sucesos), que es un tipo de objeto que analizaremos en ms detalle en el Capitulo 11. Almacenamiento de objetos. Pero ahora lo nico que necesitamos saber es que udd|) aade un objeto Event al final de la lista List, que si/.e( ) devuelve el nmero de elementos de List, que la sintaxis foreach permite extraer objetos Event sucesivos de List, y que mnove ) elimina el objeto Event especificado de List //: innerclasses/controller/Controller.java // El marco de trabaja reutilizable para sistemas de control, package innerclasses.controller; import java.til.22; public class Controller { // Una clase de ^ava.util para almacenar los objetos Event: private List<Event> eventList = new ArrayList<Event> ( ) ; public void addEvent{Event c> ( eventList.addte); ) public void rund ( while (eventList . size i ) > 0) // Hacer una copia para no modificar la lista // mientras se estn seleccionando sus elementos: for(Event e : new ArrayList<Event>(eventList))
i f(e.ready[ ) )

System. out.pri Por alguna rozn. este problema siempre me ha resultado bastante grato de resolver, proviene de mi anterior libro O+Insufe & Out. pero Java permite obtener unu solucin mas elefante. i
22

10 Clases Internas 662 ntln(e) ; e.actio n t); eventLi st.rera ove(e);

] ///:-

El mtodo run( ) recorre en bucle una copia de eventList, buscando un objeto Event que est listo para ser ejecutado, Para cada uno que encuentra, imprime informacin utilizando el mtodo toString( ) del objeto, invoca el mtodo action( ) y luego elimina el objeto Event de la lista. i

10 Clases Internas 663 Observe que en este diseo, hasta ahora, no sabemos nada acerca de <tt es exactamente lo que un objeto Event hace. Y este es precisamente el aspecto fundamental del diseo: la manera en que separa las cosas que cambian de las cosas que permanecen iguales". O. por utilizar un trmino que a mi personalmente me gusta, el vector de cambio est compuesto por las diferentes acciones de los objetos Event. y podemos expresar diferentes acciones creando distintas subclases de Event.

Aqu es donde entran enjuego las clases internas. Estas clases nos permiten dos cosas:

1.

La implementacin completa de un marco de control se crea en una nica clase, encapsulando de esa forma toda* aquellas caractersticas distintivas de dicha implementacin. Las clases internas se usan para expresar los mltiples tipos distintos de acciones (action()J necesarias para resolver el problema.

2,

Las clases internas evitan que esta implementacin sea demasiado confusa, ya que podemos acceder fcilmente a cualquiera de los miembros de la clase externa Sin esta capacidad, el cdigo podria llegar a ser tan complejo que terminaramos tratando de buscar una alternativa.

10 Clases Internas 664 Considere una implementacin concreta del marco de control diseado para regular las funciones de un invernadero. Cada accin es totalmente distinta: encender y apagar las luces, iniciar y detener el riego, apagar y encender los termostatos, hacer sonar alarmas y reimciali/ar el sistema. Pero el marco de control esta diseado de tal manera que se aslan fcilmente esuis distintas secciones del cdigo. Las clases internas permiten disponer de mltiples versiones derivadas de la misma clase base. Event. dentro de una misma clase Para cada tipo de accin, heredamos una nueva clase interna Event V escribimos el cdigo de control en la implementacin de action( ).

Como puede suponer por los marcos de trabajo para aplicaciones, la clase CreenhouseControls hereda de Controller //j innerclasses/GreenhouseControls.java // Genera una aplicacin especfica del sistem a// de control, dentro de una nica clase. Las clases // internas permiten encapsular diferente funcionalidad // para cada tipo de suceso, import innerclasses.controller.; public class GreenhouseControlo extends Controller { private boolean light * false; public class LightOn extends Event ( public LightOnHong deiayTime! ( 9 uper(deiayTime)/ } public void act ion) ( i // Poner cdigo de control del hardware aqui I f para encender fsicamente las luces, light = true;

public String toStringO ( return Light is on41*; }

} public class LightOff extends Event { public LightOff1 long deiayTime) i

10 Clases Internas 665 ( super(deiayTime); ) public void actioni) ( // Poner cdigo de control del hardware aqui i f para apagar fsicamente las luce3. light = false;

) )* oublic String toStringO ( return "Light is off"; |

private boolean water - false; public class WaterOn extends Event ( public WaterOn(long deiayTime) ( super(deiayTime); } public void act ion( ) ( // Poner el cdigo de control del hardware aqui. water = true;

) public String toStringO ( return Greenhouse water is on";

10 Clases Internas 666 ) public class WaterOff extends Event { public WaterOff(long deiayTime) { super(deiayTime); } public void action() { f t Poner el cdigo de control del hardware aqui. water = false;

) public String toStringO ( return Greenhouse water is off";

} private String thermostat * "Day"; public class ThermostatNight extends Event { public ThermostatNight(long deiayTime) { super(deiayTime) t ) public void act ionO ( f t Poner el cdigo de control del hardware aqu, thermostat * "Night"; i

10 Clases Internas 667 > public String toStringO { return "Thermostat on night setting;

) )public clase ThermostatDay extends Event { public ThermostatDay(long delayTime) ( super IdeldyTime) ,

) public void actionO ( // Poner el cdigo de control del hardware aqu, thermostat = "Day";

public String toString( ) ( retum "Thermostat on day setting";

) i // Un ejemplo de actianO que inserta un i

10 Clases Internas 668 // nuevo ejemplar de ai misma en la lnea de sucesos: public class Bell extends Event ( public Belliong delayTime) i euper(delayTime) : ) public void actionO ( i addEvent(new Bell(delayTime));

public String toStringl) ( return "Bina!"; )

) public class Restart extends Event ( private Event[] eventList; public Restart{long delayTime, Event[] eventList) ( super(delayTimeI; this.eventList - eventList; for(Event e : eventList) addEvent(e >;

) public void actionO { forEvent e ; eventList) ( e.startO; // Re-ejecutar cada suceso. addEvent(e);

10 Clases Internas 669 ) startOj // Re-ejecutar cada suceso addEvent(this);

) public String toString<) { returr. "Restartina system"/

) i public static class Termnate extends Event ( public Termnate ilong delayTime) { superi delayTime)j ) public void actionO ( System.exit 10) ; ) public String toStringO ( return MTerminatinq"; )

10 Clases Internas 670 } ///:-

Observe que light, water y thermostat pertenecen a la clase externa GreenhouseControK a pesar de lo cual las clases internas pueden acceder a dichos campos sin ninguna cualificacin y sin ningn permiso especial. Asimismo, los mtodos actinn() suelen requerir algn tipo de control del hardware.

La mayora de las ciases Event parecen similares, pero Bell y Restart son especiales. Bell hace sonar una alarma y luego aade un nuevo objeto Bell a la lista de sucesos, para que vuelva a sonar posteriormente. Observe cmo las clases internas casi parecen un verdadero mecanismo de herencia mltiple. Bell y Restart tienen todos los mtodos de Event y tambin parecen tener todos los mtodos de la clase externa GreenhouxeControls.

A Restart se le proporciona una matriz de objetos Event y aqulla se encarga de aadirla al controlador. Puesto que Restart( ) es simplemente otro objeto Event. tambin se puede aadir un objeto Restart dentro de Restart.action( ) para que el sistema se reinicialtcc a si mismo de manera peridica.

10 Clases Internas 671 La siguiente clase configura el sistema creando un objeto GreenhouseControls y aadiendo diversos tipos de objetos Event. Esto es un ejemplo del patrn de diseo Comniand: cada objeto de cventIJst es una solicitud encapsulada en forma de objeto: //: innerclasses/GreenhouseController.java // Configurar y ejecutar el sistema de control de invernadero. / / {Args: 5000} inn>ort innerclasBes.controller; public class GreenhouseController { public static void mam (String[J args) ( GreenhouseControls ge = new GreenhouseControls (I // En lugar de fijar los valores, podramos analizar // informacin de configuracin incluida t i e n un archivo de texto: ge. addEver.t (ge. new Bell (900)) ; EventU ventList = { ge.ne w Therm ostat Night (0), ge.ne w Light 0n(20 0), ge.ne w Light Off(4 00J, ge.ne w Water On(60 0), ge.ne w Water Off(8 00). i

10 Clases Internas 672 ge.ne w Therm osLat Day(1 400)

ge.addEvent(ge.new Restart2Q00, eventList)); if(args.length == 1J ge.addEvent( new GreenhouseControls. Termnate( new Integerargs[0 J)J) ; ge.run()r ) / Cutput: angl Thermostat on night setting Light is or* Light is off Greenhouse water B on Greenhouse water i uCC Thermostat on day setting Restarting svstem Terminating *///:-

siu clase iniciali/a el sistema, para que aada todos los sucesos apropiados. El suceso Kcstart se ejecuta repetidamente y carga cada vez la lista eventList en el objeto GreenhouseControls. Si proporcionamos un argumento de lnea de comandos que indique los mil segundos. Restart terminar el programa despus de ese nmero de milisegundos especificado (esto se usa para las pruebas).

Por supuesto, resulta ms flexible leer los sucesos de un archivo en lugar de leerlos en el i

10 Clases Internas 673 cdigo. Uno de los ejercicios del Capitulo 18, F./S, pide, precisamente, que modifiquemos este ejemplo para hacer eso.

Este ejemplo debera permitir apreciar cul es el valor de las clases internas, especialmente cuando se las usa dentro de un marco de control Sin embargo, en el Captulo 22, Interfaces grficas de usuario. veremos de qu forma tan elegante se utilizan las clases internas para definir las acciones de una interfaz grfica de usuario Al terminar ese captulo, espero haberle convencido de la utilidad de ese tipo de clases.

Ejercicio 24: (2) In GreenhouseControls.java. aada una serie de clases internas Event que permitan encender y apa

gar una serie de ventiladores. Configure GrcenhouseController.java para utilizar estos nuevos objetos Event.

Ejercicio 25: (3) Herede de GreenhouscControls en GreenhouseControb.java para aadir clases niemas Event i

10 Clases Internas 674 que permitan encender y apagar una serie de vaporizadores. Escriba una nueva versin de GreenhouseController.java para utili/ar estos nuevos objetos Event Cmo heredar de clases internas

Puesto que el constructor de la clase interna debe efectuar la asociacin como una referencia al objeto de la clase contenedora, las cosas se complican ligeramente cuando tratamos de heredar de una clase interna. Fl problema es que la referencia secreta al objeto de la clase contenedora Jebe in atizarse. a pesar de lo cual en la clase derivada no hay ningn objeto predeterminado con el que asociarse lis necesario utili/ar una sintaxis especial para que dicha asociacin se haga de forma explcita: //: mnerclasses/Inheritlnner. jav3 // Heredando de una clase interna. class Withlnner { class Inner {)

} public class Inheritlnner extends Withlnner.Inner { l / \ Inheritlnner() () //No se compilar Inheritlnner(Withlnner wi) ( wi.superI)/

10 Clases Internas 675 } public static void main(String(] argsl { Withlnner wi new Withlnner(); Inheritlnner ii new InheritInner(wi);

) ) f/j'.-

Puede ver que Inheritlnner slo amplia la clase interna, no la externa. Pero cuando llega el momento de crear un constructor. el predeterminado no sirve y no podemos limitamos a pasar una referencia a un objeto contenedor. Adems, es necesario utilizar la sintaxis: enclosingClassReference.super();

dentro del constructor. Esto proporciona la referencia necesaria y el programa podr asi compilarse.

10 Clases Internas 676 Ejercicio 26: (2) Cree una clase con una clase interna que tenca un constructor no predeterminado (uno que tome argu

mentos). Cree una segunda clase con una clase interna que herede de la primera clase interna. Pueden sustituirse las clases internas?

Qu sucede cuando creamos una clase interna, luego heredamos de la clase contenedora y rede fin irnos la clase interna? En otras palabras, es posible "sustituir la clase interna completa? Podra parecer que esta tcnica resultara muy til, pero el sustituir una clase interna como si fuera otro mtodo cualquiera de la clase extema no tiene, en realidad, ningn efecto //: innerclasses/BigEgg.java // No se puede sustituir una clase interna como si fuera un mtodo, import static net .mindview. til. Pnnt. # ; class Egg { private Volk y; protected class Yolk { public YolkO ( print ("Egg. Yolk ()"); }

10 Clases Internas 677 ) public EggO ( print(MNew Egg(J M J; y t* new Volk ()

i 10 Clases internas 678 i ; public class 9iggg extends Egg [ public class Yolk { public YolkO { print<"BigEgg.YolkO"); )

} public static void mainStringl] args' ( new BiaEga();

) ] /* Output: New Egg() Egg.Yolk{)

///*-

El compilador sintetiza automticamente el constructor predeterminado, y ste invoca al constructor predeterminado de la clase base. Podramos pensar que puesto que se est creando un objeto BigF.gg,

i 10 Clases internas 679 i se utilizar la versin sustituida'1 ce Nolk. pero esto no es asi, como podemos ver analizando la salida.

Este ejemplo muestra que no hay ningn mecanismo mgico adicional relacionado con las clases internas que entre en accin al heredar de la clase externa. I^as dos clases internas son entidades completamente separadas, cada una con su propio espacio de nombres. Sin embargo, lo que sigue siendo posible es heredar explcitamente de la clase interna: //: innerclasses/BigEgg2.java ! ( Herencia correcta de una clase interna. mport static net .mindview.util .Print class Egg2 { protected class Yolk ( public Yclk() { print("Egg2.Yolk()"); ) public void f() ( print(nEgg2 .Yolk. f C) ") ;)

) private Yolk y = new YolkO; public Egg2() ( print"New Egg2)n); } public void insertYclk(Yolk yy) ( y yy; } public void g() ( y.fl); }

i 10 Clases internas 680 i ) public class BigEgg2 extends Egg2 ( public class Yolk extends Egg2.Yolk ( public YolkO ( print("BiaEgg2 YolkO"J; ) public void fO ( print I "BigEgg2.Yolk.() ") / }

) public BigEgg2(i { inaertYolk(new Yolkl)); > public static void main(StringlJ args) { Egg2 e2 new BigEgg2 (); e2 .g ) i

) ) / Output: Egg2.Yolkt) New Egg2() Egg2.Yolk() BigEgg2.Yolk ) BiqEgg2.Yolk.f()

i 10 Clases internas 681 i *///:-

Ahora. BlgEgg2.Yolk amplia explicttamente extends Egg2.Yolk y sustituye sus mtodos El mtodo fnsertYo!k( ) permite que BigEgg2 generalice uno de sus propios objetos Yolk a la referencia y en Egg2. por lo que g( ) invoca y.f(). se utiliza la versin sustituida de f( ). La segunda llamada a Egg2.Yolk() es la llamada que el constructor de la clase base hace al constructor de BigEgg2.Yolk Como puede ver. cuando se llama a g( ) se utiliza la versin sustituida de f(). Clases internas locales

Como hemos indicado anteriormente, tambin pueden crearse clases internas dentro de bloques de cdigo, normalmente dentro del cuerpo de un mtodo. Una clase interna local no puede tener un especificador de acceso, porque no forma pane de la clase externa, pero s que tiene acceso a las variables finales del bloque de cdigo actual y a todos los miembros de la clase contenedora. He aqu un ejemplo donde se compara la creacin de una clase interna local con la de una clase interna annima: //: innerciasses/LocailnnerCla as.]ava // Contiene una secuencia de objetes, import static net.mindview.util.Print.; interface Counter ( nt next( ) ;

i 10 Clases internas 682 i ) public class LocalInnerClass { private int count = 0; Counter getCounter(final String ame) ( // Una clase interna local: class LocalCounter implements Counter ( public LocalCountert) ( // La clase interna local puede tener un constructor orint (''LocalCounter {) ") ;

] public int next () ( i printnb(ame); // Acceso a variable local final retura count++;

} return new LocalCounter() ;

i 10 Clases internas 683 i // Lo mismo con una clase interna annima: Counter getCounter21 final String ame) { return new Counter() { // La clase interna annima no puede tener un constructor // nominado, sino slo un inicial izador de instancia:

{ Drint("Counter()n);

) public int next() ( printnb trame) ; // Acceso a una variable local final return count--+;

) public static void main(StringU args) { LocallnnerClass lie new LocalInnerClass(); Counter cl = 1ic.getCounter("Local inner "), c2 = lie.getCounter2i"Anonymous inner "); for tint i = 0; i < 5? i++) print(cl.next I));

i 10 Clases internas 684 i i for(int i = 0; i < 5; i+*) print(c2 .next{))/

) /* Output: LocalCounter() Counter() Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner 5 Anonymous inner 6 Anonymous inner 7 Anonymous inner 8 Anonymous inner 9 ///:-

Counter devuelve el siguiente valor de una secuencia. Est implementado como una clase local y como una clase interna annima, teniendo ambaslos mismoscomportamientos y capacidades. Puesto que el nombre dela clase interna local no es

accesible fuera del mtodo, la nica justificacin parautilizaruna clase interna local de una claseinterna annima

en lugar

es que necesitemos un constructor nominado y/o un constructor sobrecargado, ya que una clase

i 10 Clases internas 685 i interna annima slo puede utili/ar un mecanismo de inicializacin de instancia.

Otra razn para definir una clase interna local en lugar de una clase interna annima es que necesitemos construir ms de un objeto de dicha clase. identificadores de una clase interna

Puesto que todas las clases generan un archivo .class que almacena toda la informacin relativa a cmo crear objetos de dicho tipo (esta informacin genera una metaclase*1 denominada objeto Class), podemos imaginar fcilmente que las clases internas tambin debern producir archivos .class para almacenar la informacin de sus objetos Class. Los nombres de estos archivos /classes responden a una frmula estricta: el nombre de la clase contenedora, seguido de un signo S*. seguido del nombre de la clase interna. Por ejemplo, los archivos .class creados por LocalinncrClass.java incluyen: Counter.class LocalInnerClassSl.class LocalInnerClass$lLocalCou nter.class LocalInnerClass.class

Si las clases internas son annimas, el compilador simplemente genera una serie de nmeros para que acten como identi- ftcadores de las clases internas. Si las clases internas estn anidadas dentro de otras clases internas, sus nombres se aaden simplemente despus de un *$ y del idcntificador o identificadores de las clases externas.

i 10 Clases internas 686 i Aunque este esquema de generacin de nombres internos resulta simple y directo, tambin es bastante robusto y permite tratar la mayora de las situaciones.5 Puesto que se trata del esquema estndar de denominacin para Java, los archivos generados son automticamente independientes de la plataforma (tenga en cuenta que el compilador Java modifica las clases internas de mltiples maneras para hacer que funcionen adecuadamente). Resumen

Las interfaces y las clases internas son conceptos bastante ms sofisticados que los que se pueden encontrar en muchos lenguajes de programacin orientada a objetos; por ejemplo, no podremos encontrar nada similar en O-h Ambos conceptos resuelven, conjuntamente, el mismo problema que C++ trata de resolver con su mecanismo de herencia mltiple. Sin embargo. el mecanismo de herencia mltiple en C-*-+ resulta bastante difcil de utilizar, mientras que las interfaces y las clases internas de Java son, por comparacin, mucho ms accesibles.

Aunque estas funcionalidades son. por s mismas, razonablemente sencillas, el uso de las mismas es una de las cuestiones fundamentales de diseo, de forma similar a lo que ocurre con el polimorfismo. Con el tiempo, aprender a reconocer ' Por otro lado. $' es un mcucaricicr de la shet! Unix, por lo que en ocasiones podemos encontramos con problemas o la hora de listar los archivos .class. Resulta un tanto extrao este problema, dado que el lenguaje Java ha sido definido por Sun. una empresa volcada en el mercado Unix, Supongo que no tuvieron en cuenta el problema, pensando en que los programadores se centraran principalmente en los archivos de cdigo fuente aquellas situaciones en las que debe utilizarse una interfaz o una clase interna, o ambas cosas. Pero al menos, en este punto del libro, s que el lector debera sentirse cmodo con Ja sintaxis y la semntica aplicables. A medida que vaya viendo cmo se aplican estas funcionalidades, terminar por interiorizarlas.

Puede encontrar las soluciones a los ejercicios seleccionados en el documento electrnico Tin Dimkiug in JUM AnnoraledStduUon Gnlda, disponible pa l.i venia en www.MlHdlrhfw.rtei.Almacenamiento de objetos

11

Es un programa bastante simple que slo dispone de una cantidad de objetos con tiempos de

vida conocidos.

Eli general, los programas siempre crearn nuevos objetos basndose en algunos criterios que slo sern conocidos en tiempo de ejecucin. Antes de ese momento, no podemos saber la cantidad ni el tipo exacto de los objetos que necesitamos. Para resolver cualquier problema general de programacin, necesitamos poder crear cualquier nmero de objetos en cualquier momento y en cualquier lugar. Por tamo, no podemos limitamos a crear una referencia nominada para almacenar cada uno de los objetos: MiTipo unaReferencia?

ya que no podemos saber cuntas de estas referencias vamos a necesitar.

La mayora de los lenguajes proporciona alguna manera de resolver este importante problema. Java

dispone de varias formas para almacenar objetos (o ms bien, referencias a objetos). El tipo soportado por el compilador es la matriz, de la que ya hemos hablado antes. Una matriz es la forma ms eficiente de almacenar un grupo de objetos, v recomendamos utilizar esta opcin cada vez que se quiera almacenar un grupo de primitivas. Pero una matriz tiene un tamao fijo y. en el caso mas general, no podemos saber en el momento de escribir el programa cuntos objetos vamos a necesitar o si har falta una forma mas sofisticada de almacenar los objetos, por lo que la restriccin relativa al tamao fijo de una matriz es demasiado limitante.

La biblioteca java.til tiene un conjunto razonablemente completo de clases contenedoras para resolver este problema, siendo los principales tipos bsicos Lisi. Set. Queue y Map (lista, conjunto, cola y mapa) Estos tipos de objetos tambin se conocen con el nombre de clases de coleccin, pero como la biblioteca Java utiliza el nombre Collection para hacer referencia a un subconjunto concreto de la biblioteca, en este texto utilizaremos el trmino ms general de contenedor Los coniencdores proporcionan formas sofisticadas de almacenar los objetos, y con ellos podemos resolver un sorprendente nmero de problemas.

Adems de tener otras caractersticas (Set. por ejemplo, slo almacena un objeto de cada valor mientras que Map es una matriz asociativa que permite asociar objetos con otros objetos), las clases contenedoras de Java tienen la funcionalidad de cambiar automticamente de tamao. Por lano, a diferencia de las matrices, podemos almacenar cualquier nmero de objetos y no tenemos que preocupamos, mientras estemos escribiendo el programa, del tamao que tenga que tener el contenedor

An cuando no tienen soporte directo mediante palabras clave de Java, 23 las clases contenedoras son herramientas fundamentales que incrementan de manera significativa nuestra potencia de programacin. En este captulo vamos a aprender los aspectos bsicos de la biblioteca de contenedores de Java poniendo el nfasis en la utilizacin tpica de los contenedores Aqu, vamos a centramos en los contenedores que se utilizan de manera cotidiana en las tareas de programacin. Posteriormente, en el Captulo 17. Anlisis detallado de fus contenedores, veremos el resto de los contenedores y una serie de detalles acerca de su funcionalidad y de cmo utilizarlos. Diverso* lenguajes como Perl. Poyton y Ruby nenen soporte nativo pan los contenedores Ksic es uno de ti casos en los que lo sobrecarga de operadores resultara conveniente Las clases contenedoras de C*H- y O producen una sintaxis ms limpia utiLi/ando la sobrecarga de operadores.
23

Genricos y contenedores seguros respecto al tipo

Uno de los problemas de utilizar los contenedores anienores a Java SE5 era que el compilador permita insertar un upo incorrecto dentro de un contenedor. Por ejemplo, considere un contenedor de objetos Apple que utilice el contenedor ms bsico general. ArrayList. Por ahora, podemos considerar que Array List es 'una matriz que se expande automticamente''. La utilizacin de una matriz \rrayList es bastante simple: basta con crear una. insertar objetos utilizando add() v acceder ellos mediante get(), utilizando un ndice: es lo mismo que hacemos con las matrices, pero sin emplear corchetes-' ArrayList tambin dispone de un mtodo size ) que nos permite conocer cuntos elementos se han aadido, para 110 utilizan inadvertidamente ndices que estn ms all del contenedor que provoquen un error (generando una excepcin Je tiempo Je ejecucin: hablaremos de las excepciones en el Captulo 12, Tratamiento Je errores mediante excepciones).

En este ejemplo, insertamos en el contenedor objetos Apple \ Orante y luego los extraemos. Normalmente, el compilador Java proporcionar una ad\erteneia. debido a que el ejemplo no usa genricos. Aqui. empleamos una anotacin de Java SE5 para suprimir esas advertencias del compilador. Las anotaciones comienzan cot un signo de *{<? y admiten un argumento; esta anotacin en concreto es a SuppressWarnings > el argumento indica que slo deben suprimirse las advertencias no comprobadas **uncheckeJ): //. Holding/ApplesAr.dOrangesWithoutGeneri es.java f / Ejemplo simple de contenedor (produce advertencias dei compilador). // (ThrowsException)

} Al final del Capitulo 15. Genricas. se incluye una explicacin sobre la gravedad de esie problema. Sin embargo, dicho capitulo tambin explica que los genricos de Java resultan tiles para olras cosas adema* de definir contenedores que sean eeuros con respecto al Upo de los datos.

import java.til.; class Apple { prvate static long counter; prvate final long id = counter-* t ; oublic long id) { retum id; )

} class Orange () public class ApplesAndOrangesWlthoutGenerics j SuppressWarninga ("unchecked"1 public etatic void main(String{] args) { ArrayList apples new ArrayList(); forint i = 0: i < 3; i-f+1 apples.add(new Apple()J; // No se impide aadir un objeto Orange: apples . add tnw Orange O) t forlint i *s 0 ; i < apples . size() ; i-*--) ((Apple)apples.get< i)).id()? i // Orange slo se detecta en tiempo de ejecucin

) / Execute .o see output) *///.- Hablaremos ms en detalle de las anotaciones Java SE5 en el Capitulo 20. Anotaciones.

as clases Apple y Orange son diferentes; no tienen nada en comn salvo que ambas heredan de Object (recuerde que si no se indica explcitamente de qu clase se est heredando, se hereda
I

automticamente de Object). Puesto que ArravIJst almacena objetos de tipo Object. no slo se pueden aadir objetos Apple a este contenedor utilizando el mtodo add( ) de ArrayList. sino que tambin se pueden aadir objetos Orange sin que se produzca ningn tipo de advertencia ni en tiempo de compilacin ni en tiempo de ejecucin. Cuando luego tratamos de extraer lo que pensamos que son objetos Apple utilizando el mtodo get( ) de ArrayList. obtenemos una referencia a un objeto de tipo Objeet que deberemos proyectar sobre un objeto Apple Entonces, necesitaremos encerrar toda la expresin entre parntesis para forzar la evaluacin de la proyeccin antes de invocar el mtodo id( ) de Apple: en caso contrario, se producir un error de sintaxis.

En tiempo de ejecucin, al intentar proyectar el objeto Orange sobre un objeto Apple, obtendremos un error en la forma de |u excepcin que antes hemos mencionado.

En el Capitulo 20. Genricos, veremos que la creacin de clases utilizando los genricos de Java puede resultar muy compleja Sin embargo, la aplicacin de clases genericas predefinidas suele resultar sencilla Por ejemplo, para definir un contenedor ArrayUxt en el que almacenar objetos Apple, tenemos que indicar ArrayLJst<Apple> en lugar de slo ArrayUst. Los corchetes angulares rodean los parmetros de tipo (puede haber ms de uno), que especifican el tipo o tipos que pueden almacenarse en esa instancia del contenedor

Con los genricos ev itamos, en tiempo de compilacin, que se puedan introducir objetos de tipo incorrecto en un contenedor He aqu de nuevo el ejemplo utilizando genricos: : holding/ApplesAndOrangesWithGene rics.java import java.til.; public class ApplesAndOrangesWithGeneri.es ( public static void main(String(] args) { ArrayList<Appie> appies = new ArrayList<Apple>O; forint i = 0 ; i < 3; i*+) appies.addInew Apple(I) ;

// Error en tiempo de compilacin: // appies addinew Orangei I); forfint i = 0 ; i < applea.sizel ; i-f+J System.out .prmtlnappies get(i) . id0)f // Utilizacin de foreach: for(Apple c : appies) j System.out .println le. id()) /

) /* Output:

0 2 0 X 2

///?-

Ahora el compilador evitar que introduzcamos un objeto Orange en appies. conviniendo el error en tiempo de ejecucin en un error de tiempo de compilacin

Observe tambin que la operacin de proyeccin ya no es necesaria a la hora de extraer los elementos

de la lista. Puesto que la lista conoce que tipos de objetos almacena, ella misma se encarga de realizar la proyeccin por nosotros cuando invocamos *et( ) Por tanto con los genricos no slo podemos estar seguros de que el compilador comprobar el tipo de los objetos que introduzcamos en un contenedor, sino que tambin obtendremos una sintaxis ms limpia a la hora de utilizar los objetos almacenados en el contenedor

El ejemplo muestra tambin que. si no necesitamos utilizar el ndice de cada elemento, podemos utilizar la sintaxisforeach para seleccionar cada elemento de la lista.

No estamos limitados a almacenar el tipo exacto de objeto dentro de un contenedor cuando especificamos dicho tipo como un parmetro genrico. La operacin de generalizacin ( upeasting) funciona de igual forma con los genricos que con los demas tipos: //: holding/GenericsAndUpcasting.jav a -mport java.til.*; class GrannySmith extends Apple (} class Gala extends Apple )) class Fuji extends Apple {) class Braeburn extends Apple {} public class GenericsAndUpcasting ( public static void main(Stringti args) ( ArrayLast<AppIe> apple6 new ArrayList<Apple> { ) apples. add (new GrannySmith.() J ; apples. add <new Gala (M apples .add \ new FujiO); apples.add(new Braeburn())/ for(Apple c : apples) System.cut.println(c);

! } /* Output; (Sample) GrannySmi th37d772e Galallb8Se7 Fuji35ce36 Braeburn 757aef *///:-

Por tanto, podemos aadir un subtipo de Apple a un contenedor que hayamos especificado que va a almacenar objetos Apple.

La salida se produce utilizando el mtodo toString( ) predeterminado de Object, que imprime el nomhrede la clase seguido de una representacin hexadecimal sin signo del cdigo hash del objeto (generado por el mtodo liashCodef )). Veremos ms detalles acerca de los cdigos hash en el Capitulo 17. Anlisis detallado de los contenedores.

Ejercicio 1: (2) Cree una nueva clase llamada (erbil con una variable int gerbUNumber que se inicializa en el cons

tructor. Aada un mtodo denominado hop( ) que muestre el valor almacenado en esa variable entera. Cree una lista ArrayList y aada objetos Gerbil a la lista. Ahora, utilice el mtodo get( ) para desplazarse a travs de la lista e invoque el mtodo hop( ) para cada objeto Gerbil.

Conceptos bsicos

La biblioteca de contenedores Java toma la idea de almacenamiento de los objetos y la divide en dos conceptos distintos, expresados mediante las interfaces bsicas de la biblioteca

Collection: una secuencia de elementos individuales a los que se aplica una o ms reglas Un contenedor List debe almacenar los demonios en la forma en la que fueron insertados, un contenedor Set no puede lener elementos duplicados y un contenedor Queueproduce los elementos en el orden determinado por una disciplina de cola (que normalmente es el mismo orden en el que fueron insertados)
1.

Map un grupo de parejas de objetos clave-valor, que permite efectuar bsquedas de valores utilizando una clase Un contenedor ArrayList permite buscar un objeto utilizando un nmero, por lo que en un cierto sentido sirve para asociar nmeros con los objetos. Un mapa permite buscar un objeto utilizando otro objeto Tambin se le denomina matriz asociativa, (porque asocia objetos con otros objetos) o diccionario (porque se utiliza para buscar un objeto valor mediante un objeto clave, de la misma forma que buscamos una definicin utilizando una palabra). Los contenedores Map son herramientas de programacin muy potentes.
2.

Aunque no siempre es posible, deberamos tratar de escribir la mayor parte del cdigo para que se comunique con estas interfaces; asimismo, el nico lugar en el que deberamos especificar el tipo concreto que estemos usando es el lugar de la creacin del contenedor. Asi, podemos crear un contenedor List de la forma siguiente: Lst<Apple> apples - new ArrayListcApple> () ,*

Observe que el contenedor ArrayList ha sido generalizado a un contenedor List, por contraste con la forma en que lo habamos tratado en los ejemplos anteriores. La idea de utilizar la interfaz es que. si posteriormente queremos cambiar la imple- mentacin. lo nico que tendremos que hacer es efectuar la modificacin en el punto de creacin, de la forma siguiente: Llst<Apple> apples * new LinkedList<Apple>();

/Vsi. normalmente crearemos un objeto de una clase concreta, lo generalizaremos a la correspondiente interfaz y luego utilizaremos la interfaz en el resto del cdigo.

Esta tcnica no siempre sirve, porque algunas clases disponen de funcionalidad adicional. Por ejemplo, LinkedList nene mtodos adicionales que no forman parte de la interfaz List mientras que TreeMap tiene mtodos que no se encuentran en la interfaz Map. Si necesitamos usar esos mtodos, no podremos efectuar la operacin de generalizacin a la interfaz ms general

La interfaz Collection generaliza la idea de secuencia, una forma de almacenar un grupo de objetos. He aqu un ejemplo simple que rellena un contenedor Collection (representado aqu mediante un contenedor Arra> List) con objetos Integer y luego imprime cada elemento en el contenedor resultante: //: holding/SimpleCollection.java itepore java.til.*; public clasa SimpleCollection { public static void mam(Scrmgti args) | Collection<Integer> c = new ArrayLlst<Inteaer>(); forint = 0 ; i < 10 ; i * * ) c. add t i} / / / Aut oboxmg fordnteger i : c) System.out.print(i ", 1M);

) ) / Output:

0, 1. 2. 3. 4, 5, 6, 7. 8. 9.

de una clase de Collection funciona- el contenedor Collection. Sin embar-

Puesto que este ejemplo solo utiliza mtodos Collection. cualquier objeto que herede r. pero ArrayList representa el tipo ms bsico de secuencia.

El nombre del mtodo add( ) sugiere que ese mtodo introduce un nuevo elemento en go, la documentacin indica expresamente que add( ) garantiza que este objeto Collection contenga el elemento especificado" Esto es asi para permitir la existencia de contenedores Set. que aaden el elemento slo si ste no se encuentra ya en el contenedor. Con un objeto ArrayList. o con cualquier tipo de List. add( ) siempre significa insertar el elemento", porque las listas no se preocupan de si existen duplicados.

Todas las colecciones pueden recorrerse utilizando la sintaxis foreach, como hemos mostrado aqu. Ms adelante en el capitulo aprenderemos acerca de un concepto ms flexible denominado teidor.

Ejercicio 2: (I) Modifique SimpleColIcction.java para utilizar un contenedor Set para c

Ejercicio 3: (2) Modifique innerclasses/Sequence.java de modo que se pueda aadir cualquier nmero de elementos Adicin de grupos de elementos

Existen mtodos de utilidad en las clases de matrices y colecciones de java.util que aaden grupos de elementos a una coleccin. l mtodo \rra\s.asl.ist( ) toma una matriz \ una lista de elementos separados por comas (utilizando vararas) y lo transforma en un objeto List Collections.addAll( ) toma un objeto Collection y una matriz o una lista separada por comas y aade los elementos a la coleccin I le aqu un ejemplo donde se ilustran ambos mtodos, as como el mtodo addAlK ) ms convencional que forma parte de todos los tipos Collection //: holding/AddingQroups.java // Adicin de grupos de elementos a objetos Collection. import java.util.; public class AddingGroups { public static void mainiStrmgll args? | Collection<Integer> collection = new ArrayLst<Integer>(Arrays.asList (1, 2, 3, 4. 5)) r IntegerU morelnts = ( 6 , 7, 8 , 9, 10 );

collection.addAll(Arrays.asList(morelnts)); // Se ejecuta bastante ms rpido, pero no se puede // construir una coleccin de esta forma: Collections.addAli(collection. 11, 12, 13, 14, 15); Collections.addAll(collection. morelnts); t i Produce una lista "respaldada* en una matri2 : List<Integer> list = Arrays.asList(16, 17, 1 $, 19, 20); list.setd, 99); // OK modificar un elemento // list.add(21); // Error de ejecucin porque la matriz // subyacente no se puede cambiar de tamao.

) ///:-

El constructor de una coleccin puede aceptar otra coleccin con el fin de utilizarla para metalizarse a si misma, asi que podemos emplear Arrayi.asLisrt( ) para generar la entrada para el constructor. Sin embargo. Collections.addAll( ) se ejecuta mucho ms rpido y resulta igualmente sencillo construir el objeto Collection sin ningn elemento y luego invocar Collections.addAII( ). por lo que sta es la tcnica que ms se suele utilizar.

El mtodo miembro Collcction.addAll( ) slo puede tomar como argumento otro objeto Collection. por lo que no es tan flexible como Arrays.asUst( ) o Collections.add\ll( ). que utilizan listas de argumentos variables.

Tambin es posible utilizar directamente la salida de Arrays.asListO como unalista, representacin subyacente en

pero

la

este caso es la matriz, que no se puede cambiar de tamao. Si tratamos de aadir o borrar elementos en dicha lista, eso impli

cara cambiar el tamao de la matriz, por lo que se obtiene un error 'Unsupported Operation" en tiempo de ejecucin.

Una limitacin de Arrays.asList( ) es que dicho mtodo trata de adivinar cul es el tipo de lista resultante, sin prestar atencin a la variable a la que la estemos asignando. lin ocasiones, esto puede causar un problema: //: holding/AsListInference.java // Arrays.asListO determina el tipo en s mismo, import java.util.*; class Snow {} class Powder extends Snow () class Light

extends Powder {} class Heavy extends Powder {} class Crusty extends Snow {} class Slush extends Snow {) public class AsListInference ( public static void main(String{| args' { List<Snow> snowi = Arrays.asList{ new Crusty(), new Slush()f new PowderIJ); // No se compilar: I t List<Snow> snow2 = Arrays.asList( // new Light(), new Heavy()); // El compilador dir: // found : java.util.Llst<Powder> i f required: java.util.List<Snow> f t Collect ions.addAllt) no se confunde: List<Snow> snow3 = new ArrayList<Snow>(); Collections.addAl1(snow3, new Light 0, new HeavyO); Proporcionar una indicacin utilizando una t f especificacin explcita del tipo del argumento:
11

ListSnow> snow4 = Arrays.<Snow>asLisc( new Light O , new HeavyO ) ;

} ///:-

.\l tratar de crear snow2. Arrays.asi Jst() slo dispone del tipo Powder, por lo que crea un objeto List<Powder> en lugar de List<Snow>. mientras que Collections.addAIK ) funciona correctamente, porque sabe, a partir del primer argumento, cual es el tipo de destino.

Como puede ver por la creacin de snow4. es posible insertar una "indicacin en mitad de Arrays.asList( ). para decirle al compilador cual debera ser el tipo de destino real para el objeto List producido por Arrays.asLisK ). Fsto se denomina especificacin explcita del tipo de argumento.

Los mapas son ms complejos como tendremos oportunidad de ver. y la biblioteca estndar de Java no proporciona ninguna forma para inicializarlos de manera automtica, salvo a partir de los contenidos de otro objeto Map Impresin de contenedores

Es necesario utilizar Arrays.toStrinj( ) para generar una representacin imprimible de una matriz, pero los contenedores se imprimen adecuadamente sin ninguna medida especial. He aqu un ejemplo que tambin nos va a permitir presentar los contenedores bsicos de Java: //: holding/PrintmgContainers. ;java /,' Los contenedores se imprimen automticamente. import j ava. til. ,* import static net.mindvlew.util.Print. *; public class Prir.tingContainers { static Collection fill(Collection<S tring> collection) { collection.add(

"rat") ; collection.add("c at"); collect ion.add ("dog"); collection.add{"d og"); return collection; i static Map fill(Map<String,S tring map) ( map.put("rat", "Fuzzy"); map. put ( "catH, "Rags1),* map.put("dog". "Boaco"); map.put("dog". "Spot"); return map;

) public static void main(String 13 args> ( print ifill(new ArrayList<String> 1 ) / print (fill(new LinkedListcString()))j print(Eill(new HashSet<String>I))): printIfill(new TreeSet<String>() ) ) ; print(fill(new LinkedHashSet<String><))); print(fill(new HashMap<String,String>())> print(fill(new TreeMap<String, String>())) ? print ifill(new LinkedHashMap<String,String O));

) } /* Output: [rat, cat, dog, dog] [rat, cat, dog, dog] [dog. cat, rat]

[cat, dog, rat] [rat. cat, dog] (dog=5pot, cat=Rags, rat=Fuzzy) (cat-Rags, dog=Spot, rat=Fuzzy) {rat=Fuzzy, cat=Rags, dog=Spot)

*///:-

Este ejemplo muestra las dos categorias principales en la biblioteca de contenedores de Java. La distincin entre ambas se basa en el numero de elementos que se almacenan en cada position del contenedor. La categora Collection slo almacena un elemento en cada posicin: esta categora incluye el objeto l.ist. que almacena un grupo de elementos en una secuencia especificada, el objeto Set. que no permite aadir un elemento idntico a otro que ya se encuentre dentro del conjumo \ el objeto Queuc, que slo permite insertar objetos en un extremo del contenedor y extraer los objetos del otro extremo (en lo que a este ejemplo respecta se tratara simplemente de otra forma de manipular una secuencia, por lo que no lo hemos incluido). Un objeto Map, por su pane, almacena dos objetos, una clave y un valar asociado, en cada posicin.

A la salida, podemos ver que el comportamiento de impresin predeterminado (que se implementa medanle el mtodo toStringt ) de cada contenedor) genera resultados razonablemente legibles. Una coleccin se imprime rodeada de corchetes, estando cada elemento separado por una coma. Un mapa estar rodeado de llaves, asocindose las claves y los valoro mediante un signo igual (las claves a la izquierda y tos valores a la derecha).

lil primer mtodo fill( ) funciona con todos los tipos de coleccin, cada uno de los cuales se encarga de implementar el mtodo add( ) para incluir nuevos elementos.

ArrayList y LinkedList son tipos de listas y, como puede vera la salida, ambos almacenan los elementos en el mismo orden en el que fueron insertados. La diferencia entre estos dos tipos de objeto no esta slo en la velocidad de ciertos tipos de operaciones, sino tambin en que la clase LinkedList contiene ms operaciones que ArrayList. Hablaremos ms en detalle de estas operaciones posteriormente en el capitulo.

HashSet. TreeSet y LinkedHashSet son tipos de conjuntos. La calida muestra que los objetos Set slo permiten almacenar una copia de cada elemento (no se puede introducir dos veces el mismo elemento), pero tambin muestra que las diferentes implementaciones de Set almacenan los elementos de manera distinta. HashSet almacena los elementos utilizando una tcnica bastante compleja que analizaremos en el Capitulo 17, Anlisis detallado de los contenedores, lo nico que necesitamos saber en este momento es que dicha tcnica representa la forma ms rpida de extraer elementos y. como resultado, el orden de almacenamiento puede parecer bastante extrao (a menudo, lo nico que nos preocupa es s un cierto objeto forma parte de un conjunto, y no el orden en que aparecen los objetos). Si el orden de almacenamiento fuera importante. podemos utilizar un objeto TreeSet, que mantiene los objetos en orden de comparacin ascendente, o un objeto LinkedHashSet, que mantiene los objetos en el orden en que fueron aadidos.

Un mapa ( tambin denominado matriz asociativa) permite buscar un objeto utilizando una clave, como si fuera una base de datos simple. El objeto asociado se denomina valor. Si tenemos un mapa que asocia los estados americanos con sus capitales y queremos saber la capital de Ohio, podemos buscarla utilizando "Ohio como clave, de forma muy similar al proceso de acceso a una matnz utilizando un ndice. Debido a este comportamiento, un objeto mapa slo admite una copia de cada clave (no se puede introducir dos veces la misma clave)

Map.put(kc). valu) aade un valor (el elemento deseado) y lo asocia con una clave (aquello con lo que buscaremos el elemento) Map.get(key) devuelve el valor asociado con una clave. En el ejemplo anterior slo se aaden parejas de clave- valor. stn realizar ninguna bsqueda. Ilustraremos el proceso de bsqueda ms adelante.

Observe que no es necesario especificar (ni preocuparse por ello) el tamao del mapa, porque ste cambia de tamao automticamente. Asimismo, los mapas saben cmo imprimirse, mostrando la asociacin existente entre claves y valores. En el orden en que se mantienen las claves y valores dentro de un objeto Map no es el orden de insercin, porque la implemcn- tacin HashMap utiliza un algoritmo muy rpido que se encarga de controlar dicho orden.

En el ejemplo se utilizan las tres versiones bsicas de Map HashMap. TreeMap y LinkedlIashMap Al igual que HashSet. HashMap proporciona la tcnica de bsqueda ms rpida, no almacenando los elementos en ningn orden aparente. Un objeto TreeMap mantiene las claves en un orden de comparacin ascendente, mientras que l.nkedHashMap tiene las claves en orden de insercin sin dejar, por ello, de ser igual de rpido que HashMap a la hora de realizar bsquedas

Ejercicio 4: (3) Cree una clase generadora que devuelva nombres de personajes (como objetos String) de su pelcula

favorita cada vez que invoque next( ), y que suelva al principio de la lista de personajes una vez que haya acabado con todos los nombres. Utilice este generador para rellenar una matriz, un ArrayList. un LinkedList. un HashSet. un LinkedHashSet y un TreeSet. y luego imprima cada contenedor.

List

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 709 Las listas garantizan que los elementos se mantengan en una secuencia concreta. La interfaz List aade varios mtodos a Collection que permiten la insercin y la eliminacin de elementos en mitad de una lista.El objeto bsico ArrayList. que es el que mejor permite acceder a los elementos de forma aleatoria, pero que resulta ms lento a la hora de insertar y eliminar elementos en mitad de una lista.

El objeto LinkcdLst. que proporciona un acceso secuencia! ptimo, siendo las inserciones y borrados en mitad de una lista enormemente rpidos. IJnkedL.ist resulta relativamente lento para los accesos aleatorios, pero tiene muchas ms funcionalidades que ArrayList

El siguiente ejemplo se adelanta un poco dentro de la estructura del libro, utilizando una biblioteca del Captulo 14, Informacin de tipos para importar typeinfo.pets. Se trata de una biblioteca que contiene una jerarqua de clases Pet (mascota). junto algunas herramientas para generar aleatoriamente objetos Pet. No es necesario entender todos los detalles en este momento, sino que basta con saber que existe: (l) una clase Pet y varios subtipos de Pet y (2) que el mtodo pet.i4rrayList( ) esttico devuelve un mtodo ArrayList lleno de objetos Pet aleatoriamente seleccionados: //: holding/ListFeatures. java mport typeinfo.pets.*; import java.util.; import static net .mmdview.util. Print. *; public class ListFeatures { public static void mam(Stringti args) ( Random rana = new Random(47); List<Pet> pets * Pets.arrayList(7); print("l: " - pets); Hmster h * new Hmster(); pets.addfh); // Cambio de tamao automtico print<H2 : " print("3:" t + pets); pets.containsh));

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 710 pets.remove(h); // Eliminar objeto a objeto Pet p = pets.get(2); print("4: " + p + " " + pecB.indexOftp)); Pet cytnric = new Cymrie (J ; print(M5:H print (*6 : // Debe ser el objeto exacto: print("7: " pets.remove(p)>; print("8 : " pets); pets.addO, new MouseO); // Insertar en un determinado ndice print ("9; + pets); List<Pet> sub * pets.subList(1, 4); print ("subList: +- sub); print CIO: pets.containsAll(sub)); Ccllections.sort(sub); // Ordenacin de la coleccin print (* sor ted subList: u -t- sub); // El orden no es importante en containsAll(J; print ("11: '* + pets. containsAll (sub) ? ; Collections.shuf fie'.sub, rand) ; // Mezclar los elementos print("shuffled subList: " +sub); print("12: " f pets.containsAll(sub)); List<Pet> copy * new ArrayList<? et>(pets); sub Arrays.asList(pets.get(1) , pets.get(4)); print("sub: " sub); copy.retainAll(sub); print(M13: ** copy); copy = new ArrayList<Pet> (pets) ; // Obtener una nueva copia copy .remo%*e <2); // Eliminar segn un ndice print (14: M - copy); copy.removeAllisub); // Slo elimina objetos exactos print("15: " + copy); copy.set(1, new Mouse O); // Sustituir un elemento print("16: " copy); copy .addAll <2, print(**17: print("18: " " sub),* // Insertar una lista en el medio copy); pets.isEmpty()); * pets.indexOficymric)); M+ pets .remove (cymnc) ) ;

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 711 pets.clear(); // Eliminar todos los elementos print ("19: print("20t " " i tpets); pets.isEmpty))/

pets.addAll(Pets.arrayList(4)); print("21 : " pets); Object [1 o = pets.toArray(); print("22: " 4o[3]); Pet[] pa = pets.toArray <r.ew PetlO]); print ("23: * -t pa[3].idt));

) } /* Output: 1: [Rat,Manx, Cymric, Mutt. Cymric, Pug] 2: [Rat,Manx, Cymric, Mutt. Cymric, Pug, Hamster] 3: true 4: Cymric 2 5: 1 6 : false 7: true 8 : (Rat,Manx, 9: [Rat,Manx,
10

Pug, Pug,

Mutt, Pug, Cymric, Mutt, Mouse, Pug, Cymric, Pug]

Pug]

subList: [Manx, Mutt, Mouse] .* true Mouse, Mutt] true true sorted subList:[Manx. 11 : 12 :

shuffled BubList: [Mouse, Manx, Mutt]

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 712 sub: [Mouse, Pug] 13 i 15: 17: 18: [Mouse, Pug] [Rat, Mutt, Cymric, [Rat, Mouse, false Mouse, Pug] Pug, Cymric, Pug] 14j [P.at, Mouse, Mutt, Pug, Cymric, Pug] 16: [Rat, Mouse, Cymric, Pug]

19; [] 20 : 22: 23; true Rat. EgyptianMau] EgyptianMau 14 21j[Manx, Cymric,

///*-

iemos numerado los lincas de impresin para poder establecer la relacin de la salida con el cdigo fuente. La primera linea de salida muestra la lista original de objetos Pe. A diferencia de las matrices, un objeto List permite aadir o eliminar ele mentos despus de haberlo creado y el objeto cambia automticamente de tamao sa es su caracterstica fundamental, M trata de una secuencia modificable. En la linea de salida 2 podemos ver el resultado de aadir un objeto Hmster El objeto se ha aadido al final de la lista
1

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 713 Podemos averiguar si un objeto se encuentra dentro de la lista utilizando el mtodo contains( ). Si queremos eliminar un objeto, podemos pasar la referencia a dicho objeto al mtodo remove(). Asimismo, si disponemos de una referencia a un objeto, podemos ver en que nmero de ndice esta almacenado ese objeto dentro de la lista utilizando nde\Of( ). como puede verse en la linea de salida 4.

A la hora de determinar si un elemento forma pane de una lista, a la hora de descubrir el ndice de un elemento y a la hora de eliminar un elemento de una lista a panir de su referencia, se utiliza el mtodo equa!s() (que forma parte de la clase ra/ Object) Cada objeto Pet se define como un objeto nico, por lo que, aunque existan dos objetos Cvinric en la lista, si creamos un nuevo objeto Cymric y lo pasamos a ndexOff ). el resultado ser -1 (indicando que no se ha encontrando el objeto); asimismo, s tratamos de eliminar el objeto con remove(). el valor devuelto ser falte Para otras clases, el mtodo equuh( ) puede estar definido de forma diferente: dos objetos String. por ejemplo, sern iguales si los contenidos de las cadenas de caracteres son idnticos. Asi que, para evitamos sorpresas, es importante ser consciente de que el comportamiento de n objeto List varia dependiendo del comportamiento del mtodo equals()

En las lineas de salida 7 y 8. podemos ver que se puede eliminar perfectamente un objeto que se corresponda exactamente con otro objeto de la lista.

Tambin resulta posible insertar un elemento en mitad de la lista, como puede verse en la linea de salida 9 y en el cdigo que la precede, pero esta operacin nos permite resaltar un potencial problema de rendimiento para un objeto LinkedList. la insercin y eliminacin en mitad de una lista son operaciones muy poco costosas (salvo por, en este caso, el propio acceso aleatorio en mitad de la lista), mientras que para un objeto ArrayList se trata de una operacin bastante costosa. Quiere esto decir que nunca deberamos insertar elementos en mitad de una lista ArrayList. y que por el contraro, deberamos emplear un objeto LinkedList en caso de tener que llevar a cabo esa operacin? De ninguna manera: simplemente significa que debemos tener en cuenta el potencial problema, y que si comenzamos a hacer numerosas inserciones en mitad de un objeto ArrayList y nuestro programa comienza a ralentizarse, podemos sospechar que el posible culpable es la implemento ion de la lista concreta que hemos elegido (la mejor forma de descubrir uno de estos cuellos de botella, como podr

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 714 ver en el suplemento http Mindllew ner Books/BetterJava. consiste en utilizar un perfilador). El de la optimizacin es un problema bastante complicado, y lo mejor es no preocuparse por l hasta que veamos que es absolutamente necesario (aunque comprender los posibles problemas siempre resulta til).

El mtodo sul)List( ) permite crear fcilmente una sublista a partir de otra lisia de mayor tamao, y esto produce de forma natural un resultado truc cuando se pasa la sublista a containsAlH ) para ver si tos elementos se encuentran en esa ista de mayor tamao. Tambin merece la pena recalcar que el orden no es importante: puede ver en las lineas de salida II y 12 que* al invocar los mtodos Collections.sort( ) y Collections.shuffle() (que ordenan y aleatorizan. respectivamente, los elementos) con la sublista sub. el resultado de contuinsAU( ) no se ve afectado. subList( ) genera una lista respaldada por la lista original. Por tanto, los cambios efectuados en la lista devuelta se vern reflejados en la lista original, y viceversa.

El mtodo retainAll( ) es. en la prctica, una operacin de interseccin de conjuntos, que en este caso conserva todos los elementos de copy que se encuentren tambin en sub De nuevo, el comportamiento resultante depender del mtodo eqmils( )

La linea de salida 1-4 muestra el resultado de eliminar un elemento utilizando su nmero ndice, lo cual resulta bastante ms directo que eliminarlo mediante la referencia al objeto, ya que no es necesario preocuparse acerca del comportamiento de cquals( ) cuando se utilizan ndices.

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 715 El mtodo removeAIK ) tambin opera de manera distinta dependiendo del mtodo equals(). Como su propio nombre sugiere, se encarga de eliminar de la lista todos los objetos que esten en el argumento de tipo List.

El nombre del mtodo set( ) no resulta muy adecuado, debido a la posible confusin con la clase Sel. un mejor nombre habra sido rcplacc'* (sustituir) porque este mtodo se encarga de sustituir eJ elemento situado en el ndice indicado (el primer argumento) con el segundo argumento.

La linea de salida 17 muestra que, para las listas, existe un mtodo uddA!l( ) sobrecargado que nos permite insertar I nueva lista en mitad de la lista original, en lugar de limitamos a aadirla ul final con el mtodo addAll( ) incluido en Collection.

Las lineas de salida 18-20 muestran el efecto de los mtodos isF.mpty() y clear( ).

Las lineas de salida 22 y 23 muestran cmo puede convertirse cualquier objeto Collection en una matriz utilizando toArray( ) Se trata de un mtodo sobrecargado, la versin sin argumentos devuelve una matriz de Object. pero si se pasa una matriz del tipo de destino a la versin sobrecargada, se generar una matriz del tipo especificado (suponiendo que los mecanismos de comprobacin de tipos no detecten ningn error). Si la matriz utilizada como argumento resulta demasiado pequea para almacenar todos los objetos de la lista List (como sucede en este ejemplo). toArray() crear una nueva matriz del tamao apropiado. Los objetos Pet tienen un mtodo id(). pudiendo ver en el ejemplo como se invoca dicho mtodo para uno de los objetos de la matriz resultante.

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 716 Ejercicio 5: ModifiqueListFeatures.java autoboxing) en lugar de parautilizar enteros(recuerdela caracterstica de

objetos Pet. y explique las diferencias que haya en los resultados.

Ejercicio 6: (2)Modifique ListFeatures.java para utilizar cadenas de caracteres en lugar de objetos Pet. y explique

las diferencias que haya en los resultados.

Ejercicio 7: (3) Cree una clase y construya luego una matriz imcializada de objetos de dicha clase. Rellene una lisia a

partir de la matriz. Cree un subconjunto de la lista utilizando sub Jst(). y luego elimine

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 717 dicho subconjim- to de la lista. Iterator

En cualquier contenedor, tenemos que tener una forma de insertar elementos v de volver a extraerlos. Despus de todo, esa es la funcin principal de un contenedor almacenar cosas. En una lista, add( ) es una de las formas de insertar elementos y gei() es una de las formas de extraerlos.

Si queremos razonar a un nivel ms alto, nos encontramos con un problema: necesitamos desarrollar el programa con el tipo exacto de contenedor para poder utilizarlo. Esto puede parecer una desventaja a primera vista, pero que sucede si escrib, mos codigo para una lista y posteriormente descubrimos que seria conveniente aplicar ese mismo cdigo a un conjunto? Suponga que quisiramos escribir desde el principio, un fragmento de cdigo de propsito general, que no supiera con que tipo de contenedor est trabajando, para poderlo utilizar con diferentes tipos de contenedores sin reescribir dicho cdigo Cmo podramos hacer esto?

Podemos utilizar el concepto de teidor (otro patrn de diseo) para conseguir este grado de abstraccin. Un iterador es un objeto cuyo trabajo consiste en desplazarse a travs de una secuencia y seleccionar cada uno de los objetos que la componen. sin que el programa cliente tenga que preocuparse acerca de la estructura subyacente a dicha secuencia. Adems, un iterador es lo que usualmente se denomina un objeto ligero: un objeto que resulta barato crear. Por esa razn, a menudo nos encontramos con restricciones aparentemente extraas que afectan a los iteradors; por ejemplo, un objeto Iterator de Java slo se puede desplazar en una direccin. No son muchas las cosas que podemos hacer con un objeto Iterator salvo:

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 718


1.

Pedir a una coleccin que nos devuelva IUI iterador utilizando un mtodo terator( ). Dicho iterador estar preparado para devolver el primer elemento de la secuencia.

2.

Obtener el siguiente objeto de la secuencia mediante next().

3.

Ver si liay ms objetos en la secuencia utilizando el mtodo hasNext()

4.

Eliminar el ultimo elemento devuelto por el iterador mediante remove().

Para ver cmo funciona, podemos volver a utilizar las herramientas de la clase Pet que hemos tomado prestadas del Capitulo 1 4, Informacin de tipos. //: haldina/Simplelteration. java import typeinfo.pets.; lmport nava.til.; public class 5impleIteration ( public static void maini.String(] args) [ List<Pet> pets = Pets.arrayList(12);

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 719 Iterator<Pet> it = pets.iterator(}; while(it.hasNextt)1 ( Pet p = it.nextM; System, out .prinMp. id () + ":" -*> p " *)

) System.out.printlnl}; // Un enfoque ms simple, siempre que sea posible: for(Pet p : pets) System.out-print (p. id () " : + p * " System.out.println (); //Un iterador tambin permite eliminar elementos: it = pets. iterator O ,* forlint i = 0 ; 1 <r 6 ; i**) ( it.next(); it.remove(); i System.out.println(pets) ;

) / Outputs O.-Rat l:Manx 2 .-Cymric 3:Mutt 4: Pug 5: Cymric 6 : Pug 7:Manx 8 :Cymric 9:Rat 10 : Egypt ianNau 11 :Kamerer 0:Rat I:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric :?ug 7:Manx 8 :Cymric 9:Rat 10:EgyptianNau 11 :Hamster [Pugj Manx. Cymric, Rat, EgyptianMau, Hamster]

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 720 //A-

Con un objeto Iterator, no necesitamos preocupamos acerca del nmero de elementos que haya en el contenedor. Los meto* dos hasNext() y nex(( ) se encargan de dicha tarea por nosotros.

Si simplemente nos estamos desplazando hacia adelante por la lista y no pretendemos modificar el propio objeto l.ist. la sintaxis fijreach resulta ms sucinta.

Un iterador permite tambin eliminar el ltimo elemento generado por next( ). lo que quiere decir que es necesario invocar a nextl ) antes de llamar a remove( ).4

Esta idea de tomar un contenedor de objetos y recorrerlo para realizar una cierta operacin con cada uno resulta muy potente y haremos un extenso uso de ella a lo largo de todo el libro.

Ahora consideremos la creacin de un mtodo displayt ) que sea neutral con respecto al contenedor

Existen dos tipos de objetos List: 11 Almacenamiento de objetos 721 utilizado: //: holdmg/CrossContamer Iteration, java import typeinfo.pets.*; import java.util. * public class CrossContainerlteration { public static void display(Iterator<Pet> it) | while(it,ha9Next{)) { Pet p = it.next()/ i i System out.print(p.id() * -r p " ");

System.out.printlni) ;

public static void main(String[J args) ( ArrayList<Pet> pets =* Pets .arrayList (8 ) ; LinkedList<Pet> petsLL = new LinkedList<Pet>(pets) ; HashSet<Pet> petsHS ^ new HashSet<Pet> (pets) ; TreeSet<Pet> petsTS = new TreeSet<Pet>(pets) ; display(pets.iterator U J ; display(petsLL.iterator I I I ; display(petsHS.iterator()) ; display(oetsTS.iterator I));

} } / Output:
0 :Rat

l:Manx 2:Cymnc 3:Mutt 4: Pug 5:Cymric 6 :Pug 7:Manx 0:Rat l:Manx 2:Cymric 3rMutt 4:Pug 5:Cymric 6 :Pug 7:Manx 4: Pug 6 :Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat 5:Cymric 2:Cymric 7:Manx l:Manx 3:Mutt 6 : Pug 4:Pug 0:Rat *///:-

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 722 Piensa en Java Observe que <lisplav() no contiene ninguna informacin acerca del tipo de secuencia que est recorriendo, lo cual nos muestra la verdadera potencia del objeto Iterator: la capacidad de separar la operacin de recorrer una secuencia de la estructura subyacente de recorrer dicha secuencia. Por esta razn, decimos en ocasiones que los iteradores unifican el acceso a los contenedores.Ejercicio 9: (4) Modifique innerclasses/Sequence.java para que Sequencc funcione con un objeto Itcrafor en lugar

de un objeto Selector

Ejercicio 10: (2)Modifique el Ejercicio 9 del Capitulo 8. Polimorfismo para utilizar un objeto ArrayList para almacenar los objetos Rodent y un iterador para recorrer la secuencia de objetos Rodent.

Ejercicio 11: (2) Escrba un mtodo que utilice un iterador para recorrer una coleccin e imprima el resultado de

toString( ) para cada objeto del contenedor. Rellene todos los diferentes tipos de colecciones con una serie de objetos y aplique el mtodo que haya diseado a cada contenedor.

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 723 Piensa en Java Listlterator

Listlterator es un subtipo ms potente de Iterator que slo se genera mediante las clases List. Mientras que Iterator slo se puede desplazar hacia adelante. Listlterator es bidireccional Tambin puede generar los ndices ce los elementos siguiente y anterior, en relacin al lugar de la lista hacia el que el iterador est apuntando, permite sustiruir el ltimo elemento listado utilizando el mtodo set() Podemos generar un iterador Listlterator que apunte al principio de la lista invocando listlterator( ). y tambin podemos crear un iterador Listlterator que comience apuntando a un ndice n de la lista invocando listlterator(n). He aqu un ejemplo que ilustra estas capacidades //: holding/Listlterat ion.java import typeinfo.pets.; import java.til.*; public clasa ListIteration ) public static void main(String[1 args) ( List<Pet> pets = Pets.arrayList(8 ); ListIterator<Pet> it * pets.listlterator(); while(it.hasNext()) System.out.print(it.next() M, " ? it .nextIndex(}
it.previouslndex() + "/ ")j System.out.println);

// Hacia atrs: while it.hasPrevious()) System.out .print (it .previous { \ . id(J M "),* System.out.printlnO; System.out.printlnpets)? it e pets.listlterator (3) , while(it.hasNextO) ( it.next(; it.set(Pets.randomPet (M ;

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 724 Piensa en Java } System.out.println(pets); I ) / Output: Rat, 1, 0; Manx, 2,1; Cymric, Cymric, 6 , S; Pug, 7, 6 ; Manx, 7 6 5 4 3 2 1 0 Rat, Manx, Cymric,Mutt, Pug, Cymric, Pug, Manx] Hmster, 3, 2 Mutt, 8 , 7; 4, 1; Pug, 5. 4;

[Rat, Manx, Cymric,Cymric, Rat, EgyptianMau, EgyptianMaul

///*-

El mtodo Pets.randomPeM ) se utiliza para sustituir todos los objetos Pet de la lista desde la posicin } en adelante.

Ejercicio 12: (3) Cree y rellene un objeto List<lnteger>. Cree un segundo objeto List<lnteger> del mismo tamao que

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 725 Piensa en Java el primero y utilice sendos objetos Listlterator para leer los elementos de la pnmera lista e insertarlos en la segunda en orden inverso (pruebe a explorar varias formas distintas de resolver este problema). UnkedList

Linkedl.ist tambin implementa la interfaz List bsica, como ArrayList. pero realiza ciertas operaciones de forma ms eficiente que ArrayList (la insercin y la eliminacin en la mitad de la lista). A la inversa, resulta menos eficiente para las operaciones de acceso aleatorio.

UnkcdlJst tambin incluye mtodos que permiten usar este tipo de objetos como una pila, como una cola o como una cola bidircccional

Algunos de estos mtodos son alias o ligeramente variantes de los otros, con el fin de disponer de nombres que resulten ms familiares dentro del contexto de un uso especifico (en particular en el caso de los objetos Queuef que se usan para imple- mentar colas). Por ejemplo. getFirstf ) \ element() son idnticos: devuelven la cabecera (primer elemento) de la lsta sin eliminarlo y generan la excepcin NoSuchElcmentException si la lista est vacia. peek() es una variante de estos mtodos que devuelve nuil si la lista est vaca.

renioveFirst( ) y remove( ) tambin son idnticos: eliminan y devuelven la cabecera de la lista, generando la excepcin NoSucliLlementLxception para una lista vaca: polI( ) es una variante que

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 726 Piensa en Java devuelve nuil si la lista est vaca

addFirst() inserta un elemento al principio de la lista.

offer( > es el mismo mtodo que add() y addl.ast( ). Todos ellos aaden un elemento al final de la lista. removeLast( ) elimina y devuelve el ltimo elemento de la lista.

He aqu un ejemplo que muestra las similitudes y diferencias bsicas entre todas estas funcionalidades. Este ejemplo no repite aquellos comportamientos que ya han sido ilustrados en ListFeatures.java: ' : holding/LinkedListFeatures, java import typeinfo.pets.*; import java.til.*; import static net.mindview.til.Print. * ; public class LinkedLListFeatures { public static void main(trina] args) { LmkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList( S ) ) print(pets); // Idnticos: print\ pets.getFirst(1: " + pets.getFirst(!); print("pets.element(}; " -

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 727 Piensa en Java pets.element(J); Slo difiere en el comportamiento con las listas vacas: print ("pets .peekO : " pets.peek{1 );
II

// Idnticos; elimina y devuelve el primer elemento: print"pets . remove () - " + pets. remrve (' ) ; print("pe:s.removeFirBt(); w + pets.removeFirst()); // Slo difiere en el comportamiento con las listas vacias: print ("pets.poli 0 : " pets .poli ()) ; print(pets); pets . addFirst (new P.at ()) ; print{"After addFirst 0: " * pets) pets . of f er (Pets. randomPet ()) print("After offer) : " + pets); pets.add(Pets.randomPet1) ) ; print ("After add(): " + pets); pets.adaLastInew Hmster i ) ) : print l "After addLLast O : " -* pets); print("pets.removeLast(J : " pets.removeLast()); i ) /* Output: [Rat, Manx, Cymric, Mutt. Pug] pets.getFirst(>: Rat pets.elemer.t{): Rae pets.peek): Rat pets.remove(); Rat pets.removeFirst() : Manx petspcll(J: Cymric (Mutt, Pug} After addFirstf): [Rat, Mutt, PugJ After offerO: [Rae,Mutt.Pug, Cymric) After addO : [Rat, Mutt, Pug, Cymric, Pug] After addLastO: [Rat, Mutt, Pug, Cymric, Pug, Hmster] pets.removeLastt): Hmster *///:-

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 728 Piensa en Java Hl resultado de Pets.arrayList( ) se entrega al constructor de LinkedList con el fin de rellenar la lista enlazada. Si analiza la interfaz Queue, podr ver los mtodos clement( ). offer( ). peek(), poll() y renmve( ) que han sido aadidos a LinkedList para poder disponer de la implemcntacin de una cola. Ms adelante en el capitulo se incluyen ejemplos completos del manejo de colas. Ejercicio 13: (3) En el ejemplo innerclassos/GreenhouseControiler.java, la clase Controller utiliza un objeto

Arraxi ist Cambie el cdigo para utilizar en su lugar un objeto LinkedList y emplee un iterador para recorrer el conjunto de sucesos.

Ejercicio 14: (3) Cree un objeto vacio LinkedList<lnteger>. Utilizando un iterador Listlterator, uada valores ente

ros a la lista insertndolos siempre en mitad de la misma. Stack

l na pila (.stack) se denomina en ocasiones "contenedor de tipo LIFOM (lasr-tn, first-out. el ltimo en entrares el primero en salir). El ltimo elemento que pongamos en la parte superior de la pila ser el

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 729 Piensa en Java primero que tengamos que sacar de lu misma, como si se tratara de una pila de platos en una cafetera.

LinkedList tienemtodos que implcmentan de forma directa la funcionalidad tambin podramosusar

de pila,por

lo

que

una lista enlazada pila

LinkedList en lugar de definir una clase con las caractersticas de una Sin embargo, definir una clase

a propsito permite en ocasiones clarificar las cosas: //: net/minexview/util/Stack.java // Definicin de una pila a partir de una lista enlazada, package net.mindview.util; import 3 ava.til.LinkedList; public class Stack<T> { private LinkedList<T> storage = new LinkedList<T>()j public void push(T v) { storage .addLFrst (v) ; ) public T peekO { return storage.getFirst()/ ) public T pop() ( return storage.removeFirst0/ } public boolean emptyO ( return storage. isEmpty ) ) public String toStringO ( return storage.toString(); )

Ejercicio 8:(I)Modifique el Ejercicio I para que utilice un iterador para recorrer la lista mientras se invoca hop( ) 730 Piensa en Java ) //)v-

Esto nos permite introducir el ejemplo ms simple posible de definicin de una clase mediante genricos, l a <T> que sigue al nombre de la clase le dice al compilador que se trata de un tipo parametrizado y que el parmetro de tipo (que ser sus* tituido por un tipo real cuando se utilice la clase) es T. Bsicamente, lo que estamos diciendo es: Estamos definiendo una pila Stack que almacena objetos de tipo T. La pila se implementa utilizando un objeto LinkedList. y tambin se define dicho objeto LinkedList para que almacene el tipo T Observe que puxh() (el mtodo que introduce objetos en la pila) toma un objeto de tipo T, mientra? que peek( ) y pop( ) devuelven el objeto de tipo T El mtodo peek( ) devuelve el elemento superior de la pila sin eliminarlo, mientras que pop( ) extrae y devuelve dicho elemento superior.

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 731 Si lo nico que queremos es disponer del comportamiento de pila, el mecanismo de herencia resulta inapropiado, porque generara una clase que incluira el resto de los mtodos de LinkedList (en el Captulo 17. Anlisis detallado de los cont- ncdotvs. podr ver que los diseadores de Java I.O cometieron este error al crear java.util. Stack i/ f : holdmg/StackTest .java import. net.mindview.til ; public class StackTest { public static void main(String[] args) { Stack<String> stack = new Stack<String>()/ or(Stnng s : "My dog has fleas".spliti " " ) l stack.pushis) ; while(istack.emptyO) System.out .orint (stack. poo (> + " "),*

) } / Output: fleas has dog My ///:-

Si quiere utilizar esta clase Stack en su propio cdigo, tendr que especificar completamente el paquete (o cambiar e! nombre de la clase) cuando cree una pila: en caso contrario, probablemente entre en colisin con la clase Stack del paquete java.util. Por ejemplo, si importamos java.util.* en el ejemplo anterior, deberemos usar los nombres de los paquetes para evitar las colisiones. //: holding/StackCaiiision.}ava import net.mindview.til.; public class StackCollision ( public static void mainiString] args) ( net.mindview.til.Stack<String> stack * new net.mindview.til.Stack<String>(I ; forString s : "My dog has fleas" .split ( ")) stack.push(s); while<Istack.empty())

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 732 System,out .print (stack.pop 11 * " System.out.println(); iava.util.Stack<String> stack2 = new java .til .Stack<Strmg> 0 t Cor(String s : "My dog has fleas".split(" ")) stack2 .pushts) ; while ( stack?. .emptyl)) System, out .Drint (stack2 .Dop ()" ");

} ) / Output: ieas has dog My flean has dog My *///;-

Las dos clases Stack tienen la misma interfaz, pero no existe ninguna interfaz comn Stack en java.util. probablemente porque lu clase origina) java.util,Stack, que estaba diseada de una forma inadecuada, ya tena ocupado el nombre. Aunque java.util.Stack existe. IJnkedLlst permite obtener una clase Stack mejor, por lo que resulta preferible la tcnica basada en net.niind> iew.til.Stack

Tambin podemos controlar la seleccin de la implementacin Stack preferida** utilizando una instruccin de importacin explcita: import net.mindview.til.Stack;

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 733 Ahora cualquier referencia a Stack luir que se seleccione la versin de net.mlndvlew.util. mientras que para seleccionar java.uril.Stack es necesario emplear una cualificacin completa.

Ejercicio 15: (4) Las pilas se utilizan u menudo para evaluar expresiones en lenguajes de programacin Utilizando

net.mindview.til.Stack, evale la siguiente expresin, donde **' significa introducir la letra siguiente en la pila* mientras que V significa extraer la parte superior de la fila e imprimirlo*'-

+U+n^-c+e4r+l+a-+i-^n+t^y-+r**u"H+e+s Set

Los objetos de tipo Set (conjuntos) no permiten almacenar ms de una instancia de cada objeto. Si tratamos de aadir ms de una instancia de un mismo objeto. Set impide la duplicacin. El uso mas comn de Set consiste en comprobar la pene, nencia. para poder preguntar de una manera sencilla si un determinado objeto se encuentra dentro de un conjunto. Debido a esto, la operacin ms importante de un conjunto suele ser la de bsqueda, asi que resulta habitual seleccionar la imple- mentacin HashSet. que esta optimizada para realizar bsquedas rpidamente

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 734 Set tiene la misma interfaz que Collection, por lo que no existe ninguna funcionalidad adicional, a diferencia de los dos tipos distintos de List En lugar de ello. Set es exactamente un objeto Collection. salvo porque tiene un comportamiento distinto (ste es un ejemplo ideal del uso de los mecanismos de herencia y de polimorfismo permiten expresar diferentes comportamientos). Un objeto Set determina la pertenencia basndose en el "valor de un objeto, lo cual constituye un terau relativamente complejo del que hablaremos en el Capitulo 17. Anlisis detallado de los contenedores

He aqu un ejemplo que utiliza un conjunto HashSet con objetos Integer: //: holdlng/SetOflnteger.java import java.Util. * ; public class SetOfInteger ( public static void mainStrlng[] aros) {

Randam rand = new Random(47); Set<Integer> intset = new HashSet<Integer>t); forint i * 0 ; i < 10000 ; i++) intset.add(rana.nextlnt(30U ; l System.out.println<intset

J / * Output: tl5, B, 23, 16, 7, 22, 9, 21, 6 , 1, 29, 14, 24, 4, 19, 26, 11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5. 0]

*///:-

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 735 En el ejemplo, se aaden diez mil nmeros aleatorios entre 0 y 29 al conjunto, por lo que cabe imaginar que cada valor ten dr muchos duplicados. A pesar de ello, podemos ver que slo aparece una instancia de cada valor en los resultados

Observar tambin que la salida no tiene ningn orden especifico. Esto se debe a que HashSet utiliza el mecanismo de hash para acelerar las operaciones; este mecanismo se analiza en detalle en el Capitulo 17. Anlisis detallado de los contenedores. El orden mantenido por un conjunto HashSel es diferente del que se mantiene en un TreeSet o en un LinkedHashSei ya que cada implementacin almacena los elementos de forma distintu. TreeSet mantiene los elementos ordenados en una estructura de datos de upo de rbol rojo-negro, mientras que HashSet utiliza una funcin de hash. LinkedHashSet tambin emplea una funcin hash para acelerar las bsquedas, pero fxirece mantener los elementos en orden de insercin utilizando una lista enlazada.

Si queremos que los resultados estn ordenados, una posible tcnica consiste en utilizar un conjunto TreeSet en lugar de HashSet: // : holding/SortedSetOfInteger.java import java.til.*; public class SortedSetOfInteger ( public static void main{String(] aras? { Random rand = new Random(47) ; SortedSetInteger intset = new TreeSet<Integer>()/ forint i * 0 ; i < 10000 ; i*+) intset.add(rand.nextlnt130)) ; System.out .prmtln intset) ;

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 736 } /* Output: {0, 1, 2, 3, 4, 5, 6 , 7, 8 , 9, 10, II. 12, 13, 14, 15. 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 20, 29]

*///-

y na de lfls operaciones ms comunes que tendremos que realizar es comprobar la pertenencia al conjunto de un determinado miembro usando contains* ). pero hay otras operaciones que nos recuerdan a los diagramas Venn que ensean en el colegio:
holding/SetOperations.jav a import java.util.;

import static net.mindview.util.Print.*; public class SetOperations { public static void main (String[] aras) ( Set<String> setl = new HashSet<String> ( '> ; Collections.addAll(setl, " A B C D E F G H I J K L" . spilt ( " )); setl.add("M); print IMH: " r setl.contains IMH" ) ) ; print(HN: t setl.contains); Set<String> set2 = new HashSet<String>I) ; Collections .addAll I set 2, "H I J K L" . split [" ")),* printl"set2 in setl: " -* setl.containsAll(set2)); setl.remove(nHM); print("setl: " + setl); print(Mset2 in seel: M setl.containsAll(set2)); setl.removeAli(set2);

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 737 pnnt("set2 removed from setl: " + setl); Collections.addAll(setl, "X Y 2".split(H M)>; print(H,X Y Z' added to setl: w setl);

} ) / Output: H: true N: false set2 in setl: true setl: [D, K, C. B. L, G, I. M, A, F, J, E] [D, C, B, G, M, A, F, E] set2 in setl: false set2 removed from setl: X Y Z added to setl: [ Z . D, C, B, G, M, A, F, Y, X. E]

*///:-

Los nombres de los mtodos resultan bastante descriptivos, y existen unos cuantos mtodos adicionales que podr encontrar en el JDK.

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 738 Generar una lista de elementos diferentes puede resultar muy til en determinadas ocasiones. Por ejemplo, suponga que quisiera enumerar todas las palabras contenidas en el archivo SetOperations.java anterior. Con la utilidad neLmindview. Text File que presentaremos ms adelante en el libro, podremos abrir un archivo y almacenar su contenido en un objeto Set: //: holding/UniqueWords.j ava import java.util.*; import net.mindview.ut i1 .* public class UniqueWords ( public static void main(String] args) { Set<String> words new TreeSetString>( new TextFile I"SetOperations.java", ,'\\W+M)); System.out.println(words);

) ) / Output: [A, B, C. Collections, D, E. F, G# H, HashSet, I, J, K, L, M, N, Output, Print, Set, SetOperations, String, X, Y, Z, add, addAll, added, args, class, contains, containsAll, false, from, holding, import, in, java, main, mindview, net, new, print, public, remove, removeAli, removed, setl, set2, split, static, to, true, util, void!

///:^

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 739 TextFile hereda de List<Strlng>. El constructor de TextFUe abre el archivo y lo descompone en palabras de acuerdo con la expresin regular " W+* que significa "una o ms letras" (las expresiones regulares se presentan en el Capitulo 13 Cadenas de caracteres). El resultado se entrega al constructor de TreeSet que aade el contenido del objeto List al conjun. lo. Puesto que se trata de un objeto TreeSet. el resultado est ordenado. En este caso, la reordenacin se realiza lexicogr. pamente de modo que las letras maysculas y minsculas se encuentran en grupos separados. Si desea realizar una ordenacin alfabtica, puede pasai el comparador String.CASE_lYSENSITIVE_ORDR (uji comparador es un objeto que establece un orden) al constructor TreeSet //: holding/niqueWordsAlphabetic.j ava // Generacin de un listado alfabtico, import j ava.uti 1 .; import net.mindview.til.*; public class UniqueWordsAlphabetic ( public static void mainString] args) ( Set<String> words * new TreeSet<String> (String. CASE_INSENSITIVE_ORDER> ,* words.addAll new TextFile("SetOperations.java", "\\W*M)); ) System.out.println{words),*

) / Output: [A. add, addAll, added, args, B, C, class, Collections, contalns, containsAll, D. E, F, false, from, G, H, HashSet, holding, I, import, in, J, java, K, L, M, mam, mindview, N, net, new, Output, Print, public, remove, removeAll, removed. Set, setl, set2, SetOperations, split, static, String, to, true, til, void, X, Y, 2]

*///:-

Los comparadores se analizaran en el Capitulo 16. Matrices

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 740 Ejercicio 16: (5) C ree un objeto Sel con todas las vocales. Utilizando el archivo l ique Words. ja va. cuente y muestre

el nmero de vocales en cada palabra de entrada, y muestre tambin el nmero total de vocales en el archivo de entrada. Map

La posibilidad de establecer correspondencias entre unos objetos y otros puede ser enormemente potente a la hora de resolver cienos problemas de programacin. Por ejemplo, consideremos un programa que permite examinar la alcatoricdad de la clase Random Idealmente. Random debera producir la distribucin perfectamente aleatoria de nmeros, pero para comprobar si esto es asi debera generar numerosos nmeros aleatorios y llevar la cuenta de cules caen dentro de cada uno de los rangos definidos. Un objeto Map nos permite resolver fcilmente el problema: en este caso, la clase sera el nmero generado por Random y el valor ser el nmero de veccs que esc nmero ha aparecido: //; holding/Statiatics.java // Ejemplo simpie de HashMap. import java.util.*; public class Statistics { public static void mainlStringU args) ( Random rand * new P.andom(47 ); Mapdnteger, Integer> m = new HashMap<Integer.Integer>O; fordnt 1 *0 ; 1 < 10000 ,* i++) f // Generar un numero entre 0 y 20: me r - rand .nextInt (20 ) ; Integer freq = m.get(r) ; m.putlr, freq * nuil ? 1 : freq 1 );

He aqu una sencilla demostracin de esta nueva clase Stack 11 Almacenamiento de objetos 741 )

i 11 Almacenamiento de objetos 742 System.out.println<m);) / Output: {15*497. 4=481, 19=464, 8=468. 11=531, 16=533, 18=478. 3=503, 7471. 12=521. 17=509, 489, 0=48l)
2-

13*506.

9=549,

6=519,

1=502,

14=477,

10=513,

5503,

///:-

pn main( ). la caracterstica de autoboxing convierte el valor int aleatoriamente generado en una referencia a Integer que puede utilizarse con el mapa llasliMap (no pueden utilizarse primitivas en los contenedores) El mtodo gel( ) devuelve nuil si la clave no se encuentra ya en el contenedor (lo que quiere decir que es la primera vez que se ha encontrado ese nmero concreto). En caso contrario, el mtodo get( ) devuelve el valor Integer asociado con esa clave, el cual tendremos que incrementar (de nuevo, la caracterstica de aurohoxing simplifica la expresin, pero en la prctica se llevan a cabo las necesarias conversiones hacia y desde Integer)

He aqu un ejemplo que nos permite utilizar la descripcin de Siring para buscar objetos Pet Tambin nos muestra cmo podemos comprobar si un determinado objeto Map contiene una clave o un valor utilizando los mtodos containsKey( ) y containsN alue( ): //: holding/PetMap.j ava import type info .pets. * import java.util.; import stat ic net .mir.dview. util. Print. ; public class PetMap (

i 11 Almacenamiento de objetos 743 public static void main(String[] args) { Map<Stnng, Pet> petMap = new HashMap<String.Pet>O; petHap.put I"My Cat", new Cat("Molly")); petMap.put<"My Dog", new Dog"Ginger")); petMap .put ( u My Hamster'*, new Hamster (Boscc'M ); print(petMap); Pet dog = petMap.get("My Dog"); print(dog); print (petMap. contamsKey f "My Dog*') ) ; print(petMap.containsValue(dog));

) } / Output: {My Cac=Cat Molly, My Hamster=Hamster Bosco, My Dog-Dog Ginger} Dog Ginger true true ///;-

Los mapas, al igual que las matrices y las colecciones, pueden expandirse fcilmente para que sean multidimensionales; basta con definir un objeto Map cuyos valores .sean tambin mapas (y los valores de esos otros mapas pueden ser. a su vez. otros contenedores o incluso otros mapas). As, resulta bastante fcil combinar los contenedores para generar estructuras de datos muy potentes. Por ejemplo, suponga que queremos llevar una lista de personas que tienen mltiples mascotas, en ese caso, lo nico que necesitaremos es un objeto Map<Person. Llsl<Pel: holdi ng/MapOfList.j ava package holding; import typeinfo.pets.; import java.util.; import static net .mmdview,util .Print. *; public class MapOfList (

i 11 Almacenamiento de objetos 744 public static Map<Person. List? extends Pet>> petPeople * new HashMap<Person, List<? extends Pet>>(); 3tatic { petPeople.put(new Person("Dawn"), Arrays.asList(new Cymric( "Molly"I,new Mutt I"Spot"))); petPeople.put(new Person( "Kate"). Arrays. asList (new Cat ("Shackleton"), new Cat("Elsie May"), new Dog("Margrett"))); petPeople.put(new Person!"Marilyn"), Arrays.asList( new PugC'Louie aka Louis Snorkelstein Dupree"), new Cat("Stanford aka Stinky el Negro"), new Cat(MPinkola"))),* pet People, put; (new Person ("Luke) . Arrays.asList(new Rat("Fuzzy), new Rat("Fizzy"))); petPeople.put(new Person("IsaacH), Arrays.asList(new Rat("Freckly")));

) public static void main (String [] args) { print("People: " -* petPeople.keyset 0 ) ; print ("Pets: " pet People .values ()); for(Person person : petPeople.keyset()) ( print (person *- * has:"); for(Pet pet : petPeople.get(person)) print(" " -f pet);

i 11 Almacenamiento de objetos 745 i } / * Output: People: fPerson Luke, Person Marilyn, Person Isaac, Person Dawn, Person Kate] Pets: [[Rat Fuzzy, Rat Fizzy], [Pug Louie aka Louis Snorkelstein Dupree, Cat Stanford aka Stinky el Negro, Cat Pinkola], (Rat Freckly], [Cymric Molly, Mutt Spot], (Cat Shackleton, Cat Elsie May, Dog Margrett]] Person Luke has; Rat Fuzzy Rat Fizzy Person Marilyn has: Pug Louie aka Louis Snorkelstein Dupree Cat Stanford aka Stinky el Negro Cat Pinkola Person Isaac has: Rat Freckly Person Dawn has: Cymric Molly Mutt Spot Pexaoii Kale litxa; Cat Shackleton Cat Elsie May Dog Margrett *///:-

Un mapa puede devolver un objeto Sel con sus claves, un objeto Collection con sus valores o un objeto Sel con las parejas clave-valor que tiene almacenadas. El mtodo kevSet() genera un conjunto de todas las claves de pel People, que se utiliza en la instruccin foreach para iterar a travs del mapa.

Ejercicio 17: (2) Tome la clase Gcrbll del Ejercicio I y cambie el ejemplo para incluirla en su lugar en un objeto Map

i 11 Almacenamiento de objetos 746 asociando el nombre de cada objeto Gerbil (por ejemplo, Fuzzy" o Spot) como si fuera una cadena de caracteres (la clave) de cada objeto Gerbil (el valor) que incluyamos en la tabla. Genere un iterador para el conjunto de claves keySet() y utilcelo para desplazarse a lo largo del mapa, buscando el objeto Gerbil correspondiente a cada clave, imprimiendo la clave c invocando el mtodo hop() del objeto Gerbil.

Ejercicio 18: (3) Rellene un mapa HashMap con parejas clave-valor. Imprima los resultados para mostrar la ordena

cin segn el cdigo hash. Ordene las parejas, extraiga la clave e introduzca el resultado en un mapa LinkedHashMup. Demuestre que se mantiene el orden de insercin.

Ejercicio 19: (2) Repita el ejercicio anterior con sendos conjuntos HashSet y LinkedHashSet.

Ejercicio 20: (3)ModifiqueelEjercicio16parallevar vocal

lacuenta decuntasveces haaparecido

cada

i 11 Almacenamiento de objetos 747 Ejercicio 21: (3) Utilizando Map<String.lnteger>, siga el ejemplo de l niqueWords.java para crear un programa que

lleve la cuenta del numero de apariciones de cada palabra en un archivo. Ordene los resultados utilizando CoIlectons.sort() proporcionando como segundo argumento String.CASE_INSENSITIVE_ORDF.R (para obtener una ordenacin alfabtica), y muestre los resultados.

Ejercicio 22: (5) Modifique el ejercicio anterior para que utilice una clase que contenga un campo de tipo String y un

campo contador para almacenar cada una de las diferentes palabras, as como un conjunto Set de estos objetos con el fin de mantener la lista de palabras.

Ejercicio 23: (4) Partiendo de Statistlcs.ja\a. cree un programa que ejecute la prueba repetidamente y compruebe si hay

i 11 Almacenamiento de objetos 748 algn nmero que tienda a aparecer ms que los otros en los resultados.

Ejercicio 24: (2) Rellene un mapa LinkedHashMap con claves de tipo String y objetos del tipo que prefiera. Ahora

extraiga las parejas, ordnelas segn las claves y vuelva a insertarlas en el mapa. Ejercicio 25: (3) Cree un objeto Map<String,ArrayList<lnteger. Utilice net.mindview. TextFile para abrir un

archivo de texto y lea el archivo de palabra en palabra (utilice "\\W+" como segundo argumento del constructor TextFile). Cuente las palabras a medida que lee el archivo, y para cada palabra de un archivo, anote en la matriz. ArrayList<Integer> el contador asociado con dicha palabra; esto es, en la prctica, la ubicacin dentro del archivo en la que encontr dicha palabra

Ejercicio 26: (4) Tome el mapa resultante del ejercicio anterior y ordene de nuevo las palabras, tal como aparecan en

i 11 Almacenamiento de objetos 749 el archivo original. Queue

Una cnl (queue) es normalmente un contenedor de tipo FIFO ifirst-in, first-out, el primero en entrar es el primero er salir). En otras palabras, lo que hacemos es insertar elementos por uno de los extremos y extraerlos por el otro, y el orden en que insertemos los elementos coincidir con el orden en que estos sern extrados. Las colas se utilizan comnmente como un mecanismo fiable para transferir objetos desde un rea de un programa a otro. Las colas son especialmente importantes en la programacin concurrente, como veremos en el Capitulo 21. Concurrencia, porque permiten transferir objetos con seguridad de una a otra tarea.

LinkedList dispone de mtodos para soportar el comportamiento de una cola e implemcnta la interfaz Queue. por lo que un objeto LinkedList puede utilizarse como implementacin de Queue. Generalizando un objeto LinkedList a Queue. este ejemplo utiliza los mtodos especficos de gestin de colas de la interfaz Queue: //: holding/QueueDemo.java // Generalizacin de un objeto LinkedList a un objeto Queue. import java.util.*; public class QueueDemo ( public static void printQ{Queue queue) { while (queue.peekO ! = nuil)

System.out.print(queue.remove() System.out.println );

"

"

)j

i 11 Almacenamiento de objetos 750 }

public static void main (String [] args) { Queue<lnteger> queue * new LmkedList<Integer> () ; Random rand new Random(47) ; for(int i * 0 ; i < 10 ; i-f-f) queue.offer(rand.nextInt(i + 10 )); printQ(queue); Queue<Character qc = r.ew LinkedList<Character> () ; for(char c : "Brontosaurus".tocharArrayo) qc.offer c); printQ(qc);

} } /* Output: 8 1 1 1 5 1 4 3 1 0 1 B r o n t o s a u r u s

offer( ) es uno de los mtodos especficos de Queue: este mtodo insena un elemento al final de la cola, siempre que sea posible, o bien devuelve el valor false. Tanto peek( > como e!ement() devuelven la cabecera de la cola sin eliminarla, pero peek( ) devuelve nuil si la cola esta vacia y elemente) genera NoSuchElcmcntException Tanto poU() como removc() eliminan y devuelven la cabecera de la cola, pero poll( ) devuelve nuil si la cola est vaca, mientras que remove() genera NoSuch'lementF.xception

i 11 Almacenamiento de objetos 751 La caracterstica de aittoboxing convierte automticamente el resultado int de ne\tlnt( ) en el objeto Integer requerido por queue. v el valor char c en el objeto Charactcr requerido por qc La interfaz Queue limita el acceso a los mtodos de LinkedList de modo que slo estn disponibles los mtodos apropiados, con lo que estaremos menos tentados de utilizar los mtodos de LinkedList (aqu, podramos proyectar de nuevo queue para obtener un objeto LinkedList. pero al menos nos resultar bastante ms complicado utilizar esos mtodos).

Observe que los mtodos especficos de Queue proporcionan una funcionalidad completa y autnoma. F.s decir, podemos disponer de una cola utilizable sin ninguno de los mtodos que se encuentran en Collection. que es de donde se lia heredado

Ejercicio 27: (2) Escrba una clase denominada Command que contenga un objeto String y que tenga un mtodo ope-

ration( ) que imprima la cadena de caracteres. Escrba una segunda clase con un mtodo que rellene un objeto Queue con objetos Command y devuelva la cola rellena Pase el objeto Queue relleno a un meto- do de una tercera clase que consuma los objetos de la cola e invoque sus mtodos opera!ion( ). PriorityQueue

El mecanismo FIFO (First-in, first-out) describe la disciplina de gestin de colas ms comn. La

i 11 Almacenamiento de objetos 752 disciplina de gestin de colas es lo que decide, dado un grupo de elementos existentes en la cola, cul es el que va a continuacin. La disciplina FIFO dice que el siguiente elemento es aquel que haya estado esperando durante mas tiempo.

Por el contraro, una cola con prioridad implica que el elemento que va a continuacin ser aquel que tenga una necesidad mayor (la prioridad ms alta). Por ejemplo, en un aeropuerto, puede que un cliente que est en medio de la cola pase a >er atendido inmediatamente si su avin est a punto de salir. Si construimos un sistema de mensajera, algunos mensajes sern mas importantes que otros y sera necesario tratar esos mensajes antes, independientemente de cundo hayan llegado. El contenedor PriorityQueue ha sido aadido en Java SF.5 para proporcionar una implementacn automtica de este tipo de comportamiento.

Cuando ofrecemos, como mtodo offcr( ). un objeto a una cola de tipo PriorityQueue. dicho objeto se ordena dentro de l.i cola-' El mecanismo de ordenacin predeterminado utiliza el orden natural de los objetos de la cola* pero podemos modificar dicho elemento proporcionando nuestro propio objeto Comparator La clase PriorityQueue garant za que cuando se invoquen los mtodos peek( ). poll( ) o reniove( ). el elemento que se obtenga ser aqul que tenga la prioridad mas .ilt.i

Kesulta trivial implementar una cola de tipo PriorityQueue que funcione con tipos predefinidos como Integer, Strinj* < Charactcr En el siguiente ejemplo, el primer conjunto de valores son los valores aleatorios del ejemplo anterior, con I' cual podemos ver cmo se los extrae de manera diferente de la cola PriorityQueue //: holding/PriorityOueueDemo . java import j avaUtil.*; public elase PriorityQueueDemo | public static void mam(String[] args) {

i 11 Almacenamiento de objetos 753 PnorityQueue< Integer > priorityQueue = new PriorityQueue<Integer>O; Random r^nd ^ new Randomi47)/ " F.Iu depende, tic hecho. de ht implemcnwcin. Lo.% algoritmo de colas con prioriiiad huelen realizar Iti ordenacin durante la insercin (monten tcnilv unn estructuro de memoria que *e conoce con el nombre de cmulo), pero lumbien puede perfectamente seleccionarse et elemento nutt. importante cu * roomenlo de la extraccin La eleccin del algoritmo podra tener MI importancia si la pnondad le IH objeto* puede modificarse mientras estos estn C>|H raudo en tu cola. for <int i * 0 ; i < 10: priorityQueue.offeri rand.nextlnt(i * 20 ) i ; QueueDemo.printQi priorityQueuel; List<Integer> ints * Arrays.asList(25. 22, 20, 18, 14, 9, 3, 1, 1, 2. 3, 9, 141 18, 21, 23, 25); priorityQueue = r.ew PriorityQueueInteger> ints); QueueDemo.prmrQpriorityQueue) priorityQueue = new PriorityQueueIntegeri ints.sise O, Collections.reverseOrder( ) ) ; priorityQueue.addAH i ints) ; QueueDemo.printQ(priorityQueuei i String act = "EDDCATI0N SHOULD ESCHEW OBFUSCATION* ; List<String strings Arrays.asList (fact.split ("") J ; PriorityQueueString stringPQ ^ new PriorityQueue<Strmg> (strings) ; QueueDemo.printQ(stringPQ); stringPQ * new PriorityQueue<String>t strings.sisei), Collections.reverseOrder()) ; stringPQ.addAll(stringsI ; QueueDemo.printQ{stringPQ); Set<Character charSet new HanhSetCharacter>(); forlchar c : fact.toCharArrayi)i charSet.add(el; // Autoboxing PriorityQueue<Character> characterPQ = new PriorityQueuecCharacter><charSet); QueueDemo. DrmtQ( characterPQ) ;

i 11 Almacenamiento de objetos 754 ) ) / Output:


0 1

1 1 1 1 1

3 5 6 14 21 22 23 9 3 3 25 25 2 1 1

1 2 3 3 99 14 14 18 18 20

25 25 23 2221 20 18 18 14 14 9

A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W W U T T S S S O O O O N N L I 1 H H F E E E D D C C C B A A A B C D E F H X L H O S T U W *///:-

Como puede ver. se permiten los duplicados, los valores menores tienen la prioridad ms alta (en el caso de String. los espacios tambin cuentan como valores y tienen una prioridad superior a la de las letras) Para ver cmo podemos verificar la ordenacin proporcionando nuestro propio objeto comparador, la tercera llamada al constructor de ProritvQueue<lntcger> y la segunda llamada a Priorit>Queue<Strinu> utilizan el comparador de orden inverso generado por Collections.reverseOrdcr( ) (aadido en Java SE5),

La ltima seccin aade un conjunto llashSet para eliminar los objeios Charaeter duplicados, simplemenie con el fin de hacer las cosas un poco ms interesantes

Integer. String y Characler funcionan con PriorityQueue porque estas clases ya tienen un orden natural predefinido Si queremos utilizar nuestra propia clase en una cola PriorityQueue. deberemos incluir una funcionalidad adicional para generar una ordenacin natural, o bien proporcionar nuestro objeto Comparator. En el Capitulo 17. Anlisis detallado de los contenedores se proporciona un ejemplo ms sofisticado en el que se ilustra este mecanismo.

i 11 Almacenamiento de objetos 755 Ejercicio 28: (2) Rellene la cola PriorityQueue (utilizando offer( )) con valores de tipo Douhle creados utilizando

java.mil.Random. y luego elimine los elementos con poII() y visualcelos

Ejercicio 29: (2) Cree una clase simple que herede de Object y que no contenga ningn nombre y demuestre que se

pueden aadir mltiples elementos de dicha clase a una cola PriorityQueue Este tema se explicar en detalle en el Capitulo 17. Anlisis detallado de tos contenedores. Comparacin entre Collection e Iterator

( olleciion es Id interfaz raz que describe las cosas que tienen en comn todos los contenedores de secuencias. Podramos considerarla como una especie de interfaz incidental'*, que surgi debido a la existencia de aspectos comunes entre lus otras interfaces. Adems, la clase java.util.AbstractCollection proporciona una implementacion predeterminada de Collection. para poder crear un nuevo subtipo de AbstractCollection sin duplicar innecesariamente el cdigo

i 11 Almacenamiento de objetos 756 Un argumento en fav or de disponer de una interfaz es que sta nos permite crear cdigo genrico. Escribiendo como una interfaz en lugar de como una implementacion. nuestro cdigo puede aplicarse a ms tipos de objetos.6 Por tanto, si escribimos un mtodo que admita un mtodo Collection. dicho mtodo podr aplicarse a cualquier tipo que implemcnte Collection. y esto permite implementar Collection en cualquier clase nueva para poderla usar con el mtodo que hayamos escrito. Resulta interesante resaltar, sin embargo, que la biblioteca estndar 0*-+ no dispone de ninguna clase base comn para sus contenedores: los aspectos comunes entre los contenedores se coasiguen utilizando iteradores. En Java, podra parecer adecuado seguir la tcnica utilizada en C++ y expresar los aspectos comunes de los contenedores utilizando un iterador en lugar de una coleccin. Sin embargo, ambos enfoques estn entrelazados, ya que implementar Collection tambin implica que deberemos proporcionar un mtodo iterator(): f /: holding/InterfaceVsIterator.jav a import typeinfo.pets. import java.til.*; public class InterfaceVsIterator { public static void display(Iterator<Pet> it) ( while(it.hasNext(}J { Pet p = it. next {) ; System.out.pnnt (p.idt) H: " + p " w);

) System.out.println():

) public static void display(Collection<Pet> pete) { fortPet p : pets) System.out .print (p. id() + System. out. prmtln () ; p + " n) ;

i 11 Almacenamiento de objetos 757 ) public static void main(String[] args) ( List<Pet> petList = Pets.arrayList(8 ); Set<Pet> petSet = new HashSet<-Pet> (petList) ,* Map<String.Pet> petMap = new LinkedHashMap<String,Pet>()/ StringH ames = ("Ralph, Eric, Robin, Lacey, " -* "Britney, Sam, Spot, Fluffy">.split(", forint i = 0 ; i < ames.length; i+t) petMap.put(ames tiJ , petList.get(i)); display(petList); display (petSet) display(petList.iterator()); display (petSet. iterator O ) ,* System.out.printlnlpetMap); System.out.println(petMap.keySet()) ;

display(petMap.vales() I ; display(petMap.vales().iterator());

) } /* Output: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6 :Pug 7:Manx 4:Pug :Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric OrRat * Alguna?. persona* defienden IM creacin automtica de una interfaz para toda posible combinacin de mtodos en una cla.se; en ocasiones, defienden que >e haga calo pan odas las clases. En mi opinin, una interfaz debera tener un significado mayor que unu mera duplicacin mecnica de combinaciones de mtodos, por lo que suelo preferir esperar y ver que valor aadira una inierfa/ antes

i 11 Almacenamiento de objetos 758 de crearla o Rat IsManx 4 : Pug 2:Cymric 0:P.at 2:Cymric3:Mutt 4: Pug5:Cymric 6 : Pug 7:Manx 6 : Pug 3:Mutt 1 :Manx 5:Cymric 7:Manx

(fialph=Rat. Eric=Manx, Robin=Cymric, Lacey=Mutt# Britney=Pug, Sam=Cymric, Spot=Pug. Fluffy=Manx) [Ralph, Eric, Robn, Lacey, Britney, Sam, Spot. Fluffy] Q:Hat 0:Rat I:Manx 2:Cymric3:Mutt liManx 2:Cymric3:Mutt 4:Pug5:Cyroric 6 :Pug 4:Pug5:Cymric 6 :Pug 7:Manx 7:Manx

*///-*-

Ambas versiones de display( ) funcionan con objetos Map y con subtipos de Collcction. y tanto la interfaz Collection como Itcrator penmten desacoplar los mtodos displuy(). sin forzarles a conocer ningn detalle acerca de la implementacin concreta del contenedor subyacente.

En este caso, los dos enfoques se combinan bien De hecho. Collectinn lleva el concepto un paso ms all porque es de tipo Iterahli' y por tanto, en la implementacin de dsp!ay(Cu!li*ctiun) podemos usar la estructura /oreadt. lo que hace que el cdigo sea algo tn;> limpio

i 11 Almacenamiento de objetos 759 El uso de Iterator resulta muy recomendable cuando se implementa una clase externa, es decir, una que no sea de tipo Collcction. ya que en ese caso resultara difcil o incomodo hacer que esa clase iinplementara la interfaz Collection Por ejemplo, si creamos una implementacin de Collcction heredando de una clase que almacene objetos Pet. deberemos imple* nientar iodos los mtodos de Collection. incluso aunque no necesitemos utilizarlos dentro del mtodo display( ) Aunque esto puede llevarse a cabo fcilmente heredando de AbstractCollection, estaremos forzados a tmplemeritar iterator( ) de todas formas, junto con sizet ). para proporcionar los mtodos que no estn implementados en AbstractCollection. pero son Utilizados por los otros mtodos de AbstractCollection: //: holding/CollectionSequence .java .mporr typeinf.pets.*; import ]a va.uLil.*; public class ColleerionSequence extend AbstiaetColiection<-Pet> { private Pee ti pets = Pets.createArray(8 ); publlc lnt slzeP { retum pets.lenath; ) puble lterator<;Pet> iterator() { retum new Iterator<Pet> () { private int ndex * 0 ; pufclic boolean hasNext) { return ndex ? pets.length; i public Pet nextO { return pets Undex-t-^ J ; } public void removed ( // Ko impiementado throw new UnsupportedOperationException);

) b

} public static void main(String[] axgs) {

i 11 Almacenamiento de objetos 760 CollectionSequence c = new CollectionSequenceO; InterfaceVsrterator.display(c); InterfaeeVsIterator.display(c.iterator)I;

) ) /* Output: 0:Rat 1:Manx 2: Cymrlc 3:Mutt 4:Pug 5:Cyraric 6 :Pug 7:Manx 0:Rat 1:Manx 2:CymriC 3:Mutt 4:Pug 5:Cymric 6 :Pug 7:Manx ' / / / : -

lil mtodo remove( ) es una operacin opcional", de la que hablaremos mas delante en el Capitulo 17. Anlisis detallado de los awtenedores Aqu, no es necesario implementarlo y, si lo invocamos, generar una excepcin.

En este ejemplo, podemos ver que si implementamos Collcction. tambin implemcntamos iterator( ). adems, vemos que implementar nicamente lterator( ) slo requiere un esfuerzo ligeramente menor que heredar de AbstractCollection Sin embargo, -.i nuestra clase ya hereda de otra clase, no podemos heredar de AbstractCollection. F.n tal caso, para implemen- tur Collection seria necesario implementar todos los mtodos de la interfaz. En este caso, resultara mucho ms sencillo heredar y aadir la posibilidad de crear un llorador: //: holding/NonCollectionSequence.java import typeinfo.pets.*; import java.til.*; class PetSequence { protected Petf] oets * Pets.createArray18);

i 11 Almacenamiento de objetos 761 i public class NonCollectior.Sequence extends PetSequence ( public Iterator<Pet> iterator0 ( return new Iterator<Pet>O { orivate lnt Index = 0 ? public boolear. hasNext() { return index < pets.length; i public Pet nextII ( return petslindexn-] ) public void removed ( // No impismentado throw new nsuoportedOperationExceptionO ;

) Yi

) public static void mam (St ring [] args) ( NonCollectionSequence nc new NonCollectionSequence(); InterfaceVsIterator.display(nc.iteratorO ); 1 ) /* Output: 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6 :?ug 7:Manx ///:-

Generar un objeto Iterator es la forma con acoplamiento ms dbil para conectar una secuencia con un mtodo que consuma esa secuencia; adems, se imponen muchas menos restricciones a la clase correspondiente a la secuencia que si imple- mentumos Collection

i 11 Almacenamiento de objetos 762 Ejercicio 30: (5)ModifiqueCnllectionSequence.javaparaque AbstractCollection. sino que implemcnte Collection. La estructura foreach y los iteradores no herede de

Hasta ahora, hemos utilizado principalmente la sintaxis foreach con las matrices, pero dicha sintaxis tambin funciona con cualquier objeto de tipo Collection I lemos visto algunos ejemplos en los que empleaba ArrayList, pero he aqu una demostracin general: //: holding/ForEachCollections .java // All collections work with foreach. import java.til.*; public class ForEachCollections ( public static void mairuStringU args) { Collection<String> es = new LinkedList<String>O ; Collections.addAll(es, "Take the long way hotne".split(" ")); for(String s : es) i System, out .print (H * " s + 1,1 "i;

) /* Output: 'Take' 'the' long* way' 'home*

11 Almacenamiento de objetos 763 *///:-Como es es tina coleccin, este cdigo demuestra que todos los objetos Collcction permiten emplear la estructura /breach.

La razn de que este mtodo funcione es que en Java SE5 se lia introducido una nueva interfaz denominada Itcrahlc que contiene un mtodo iterator^ ) para generar un objeto Iterator, y la interfaz Itcrahlc es lo que la estructura foreach utiliza para desplazarse a travs de una secuencia. Por tanto, si creamos cualquier clase que mplemente Itcrahlc. dicha clase podr ser utilizada en una instruccin foreach: //: holding/IterableClass.java ft Anything Iterable works with foreach. import java.util.*; public class IterableClass implements Iterable<Strina> { protected String fl words * '"And that is how * Mwe know the Earth to be shapedsplit{" "); public Iterator<String> iterator!i Iterator<String>u ( private 0 ; public boolean hasNext1 1 index < words . length,* banana( return new int index = { return

public String next!) { retum words [ index++1 ; ) public void remove 0 { // No implementado throw new UnsuoportedOperat ionException (1 ,*

} I;

11 Almacenamiento de objetos 764 public static void main(String(] args) ( for(Stnng s r new IterableClass()I System.out .print (s " *) ;

) } /* Output! And that is how we know the Earth to be banana-shaped.

>//:-

El mtodo lterator( ) devuelve una instancia de una implementacin interna annima de Iterator<String> que devuelve cada palabra de la matriz En main( ). podemos ver que IterableClass funciona perfectamente en una instruccin foreach.

En Java SE5. hay varias clases que se han definido como Itcrahlc. principalmente todas las clases de tipo Collection (pero no las de upo Map). Por ejemplo, este codigo muestra todas las variables del entorno del sistema operativo: 7: holding/EnvironmentVariables.java import java.til.*; public class EnvironmentVariables { public static void main(String(] args' {

11 Almacenamiento de objetos 765 for(Map.Entry entry: System.getenv().entrySet(): { System, out. print In (entry, get Key 0 * "2 N entry.getValue()l ;

>

} ) / Execute to see output) ///:-

Systcm.getenv( ) devuelve un objeto Map. cntrySet( ) produce un conjunto de elementos Map.Entry v un conjunto (Set) de tipo Itcrahlc. por lo que se le puede usar en un bucle foreoch.

La instruccin foreach funciona con una matriz o cualquier cosa de tipo Itcrahlc, pero esto no quiere decir que una matriz sea automticamente de tipo Itcrable. m tampoco que se produzca ningn tipo de mecanismo de auroboxing: E*to no taba disponible units tie Java SE5. porque *e pensaba que tffttafa acoplado de una maneta demasiado entecha con el sistema operativo, lo que violaba la regla de "escribir k programa una ve/ y ejecutar 1 us en cualquier lugar" f-.l hecho de que se haya incluido ahora mugiere que los discadore. de Java h.tn decidido ver m pragmtico*. //: holdmg/ArraylsNotIterable. java import j ava * ut i1.;

11 Almacenamiento de objetos 766 public class ArraylsNotlterable { static <T> void test(Iterable<T> ib) { forlT t : ib) System.out.print(t + " ");

) public static void main (String [] args) { test(Arrays.asList(1, 2, 3)); String[] strings = { -A, // Una matriz Iterable: funciona CM )f con foreach, pero no es de tipo

//i testistrings); // Hay que convertirla explcitamente al tipo Iterable: test(Arrays.asList(strings));

) } / Output: 1 2 3 A 5 C *///:-

Al tratar de pasar una matriz como argumento de tipo Iterable el programa falla. No hay ningn tipo de conversin automtica a Iterable; sino que debe realizarse de forma manual. Ejercido31: (3) Modifique polymorphism/shape/RandoniShapeCenerator.java para hacerlo de tipo Iterable.

11 Almacenamiento de objetos 767 Tendr que aadir un constructor que admita el numero de elementos que queremos que el iterador genere antes de pararse. Verifique que el programa funciona. El mtodo basado en adaptadores

Qu sucede si tenemos una clase existente que sea de tipo Iterable. y queremos aadir una o ms formas nuevas de utilizar esta clase en una instruccin foreach1} Por ejemplo, suponga que queremos poder decidir si hay que iterar a travs de una lista de palabra* en direccin directa o inversa. Si nos limitamos a heredar de la clase y sustituimos el mtodo itera- tor(). estaremos sustituyendo el mtodo existente y no dispondremos de la capacidad de opcin.

Una solucin es utilizar lo que yo denomino Mtodo basado en adaptadores. Cuando se dispone de una interfaz y nos hace falta otra, podemos resolver el problema escribiendo un adaptador. Aqu, lo que queremos es aadir la capacidad de generar un iterador inverso, pero sin perder el iterador directo predeterminado, as que no podemos limitamos a sustituir el mtodo, lin su lugar, lo que hacemos es aadir un mtodo que genere un objeto Iterable que pueda entonces utilizarse en la instruccin foreach. Como puede ver en el ejemplo siguiente, esto nos permite proporcionar mltiples formas de usar foreach'. //: holding/AdapterMethodldiom.java // El mtodo basado en adaptadores permite utilizar // foreach con tipos adicionales de objetos itarables. import java.til.*; class P.eversibleArrayList<T> extends ArrayList<T> { public ReversibleArrayList(Collection<T> c) { super(c); ) public Iterable<T> reversedO { return new Iterable<T>() ( public Iterator<T> iterator() { return new Iterator<T>O ( int current = sizeO - 1; public boolean hasNextO ( return current > -1; ) public T nextO { return getcurrent); ) public void removeO ( // No implementado throw new

11 Almacenamiento de objetos 768 UnsupportedOperationException();

}>

) public class AdapterMethodldiom { public static void main(String[] args) ( ReversibleArrayList<String> ral = new ReversibleArrayList<String> ( Arrays .asList I "To be or not to be*. split (" **))); // Toma el iterador normal va iterator(l3 for(String s : ral) System.out.print(s " "); System. out. print In {) f* // Entregar el objeto Iterable de nuestra eleccin for(String s : ral.reversed()) System.out.print(s " H);

11 Almacenamiento de objetos 769 ) } /* Output: Te be or not to be be to not or be To

*///:-

ii nos

limitamos a poner el objeto ral dentro de la instruccin foreach. obtenemos el iterador directo (predeterminado). Pero i invocamos reversed( ) sobre el objeto, el comportamiento ser distinto.

Jtilizando esta tcnica, podemos aadir dos mtodos adaptadores al ejemplo ltcrablcClass.java: //: holding/MultilterableClass.jav a // Adicin de varios mtodos adaptadores, import java.util.*; public class MultilterableClass extends IterabieClass { public Iterable<String> reversed() ( return new Iterable<String>() ( public Iterator<Strmg> iterator0 { return new Iterator<String>() ( int current words.length - 1; public boolean hasNext() { return current > -lj ) public String next(){ return words f cur rent.--J ; } public void remove () { // No implementado throw new UnsupportedOperationException();

11 Almacenamiento de objetos 770 ) ):

};

) public Iterable<String> randomizedO { return new Iterable<String>() ( public Iterator<String> iterator() ( List<String> shuffled new ArrayList<String>(Arrays.asList(words)); Collections.shuffle(shuffled, new Random(47)); return shuffled.iterator();

11 Almacenamiento de objetos 771 ) public static void main(String 11 args) { MultilterableClass mic = new MultilterableClass{); for(String s : mic.reversed0 ) System.out .print (s " *); System.ouc.prlntln ti; for(String s : mic.randomized OJ Systetn.out .print (s t- " M); System.out.printIn() for i String s : mic) System .out .print <s *r " i ] I* Output: banana-shaped. be to Earth, the know we how is that And is banana-shaped. Earth that how the be And we know to And that is how we know che Earth to be bananashaped.

///=-

Observe que el segundo mtodo, random ). no crea su propio Iterator sino que se limita a devolver el correspondiente a la lista List mezclada.

Puede ver a la salida que el mtodo Collcctions.shufflel ) no afecta a la matriz original, sino que slo mezcla las referencias en shuffled. Esto es asi porque el mtodo randomized() empaqueta en el nuevo objeto ArrayList el resultado de Arrays.asList( ) Si mezclramos directamente la lista generada por

11 Almacenamiento de objetos 772 Arrays.asLlst( ) se modificara la matriz subyacente, como puede ver a continuacin: /(: holding/ModifymaArraysAsLis t. java import java.util. ; public class ModifyingArraysAsList { public static void main(StringH args) { Random rand new Random(47); Integerll ia = ( 1, 2, 3, 4, 5, 6, 1, 8r 9, 10 }; List<Integer> listl = new ArrayList<Integer>(Arrays.asList(ia)); System.out.printIn("Before shuffling: " + listl); Collections.shuffle(listl, rand); System, out .print In ( "After shuffling: * -* listl); System.out.println("array: " + Arrays.toString(ia) 1 ; List<Integer> list2 = Arrays.asList(ia); System.out.println("Before shuffling: + last2); Collections.shuffleilist2, rand); System.out.println("After shuffling: " > list2) r System.out.printlnI"array: " Arrays.tdSrring();

} | /* Output: Before shuffling: [1, 2, 3, 4. 5, 6, 7, 8, 9. 10)

///:-

11 Almacenamiento de objetos 773 En el pnmer caso, la salida de Arrays.asList( ) se entrega al constructor de Arra>List( ). v esto crea una lista ArrayList que hace referencia a los elementos de ia Mezclar estas referencias no modifica la matriz. Sin embargo, si utilizamoj* directamente el resultado de Arrays.asUst(a). el mezclado modifica el orden de ia. Es importante tener en cuenta que Arrays.asList( ) genera un objeto l-ist que utiliza la matriz subyacente como implementacin tsica Si hacemos algo a

ese objeto List que lo modifique y no queremos que la matriz original sevea afectada, entonces necesario realizar una

ser

copia en otro contenedor. Ejercicio 32: (2) Siguiendoel ejemplo de MultilterableClass. aada mtodos reversed( ) y randomi/cd( ) a

11 Almacenamiento de objetos 774 NonC'olleetionSequenee.java. Haga tambin que NonCollectionSequence impicmente Itcrablc y muestre que las distintas tcnicas funcionan en las instrucciones foreach.Resumen

Java proporciona varias formas de almacenar objetos:

1.

Las matrices asocian ndices numricos con lo* objetos. Almacenan objetos de un tipo conocido, asi que no es necesario proyectar el resultado sobre ningn otro tipo a la hora de buscar un objeto Pueden ser multidimensio- nales y tambin almacenar tipos primitivos. Sin embargo, su tamao no puede modificarse despus de haberlas creado.

2.

Las colecciones (Colleciion) almacenan elementos independientes, mientras que los mapas (Mnp) almacenan parejas asociadas. Utilizando los genricos de Java, especificamos el tipo de objeto que hay que almacenar en los contenedores, con el fin de no introducir un tipo incorrecto en un contenedor y tambin para no tener que efectuar una proyeccin de los elementos en el momento de extraerlos de un contenedor Tanto las colecciones como los mapas cambian automticamente de tamao a medida que aadimos ms elementos. Los contenedores no permiten almacenar tipos primitivos, pero el mecanismo de autoboxing se encarga de producir las primitivas a los tipos envoltorio almacenados en el contenedor.

Al igual que una matriz, una lista (List) tambin asocia ndices numricos con objetos; por tanto, las matrices y las listas son contenedores ordenados.

11 Almacenamiento de objetos 775


4.

Utilice una lista ArrayList si tiene que realizar numerosos accesos aleatorios: pero, si lo que va a hacer es un gran nmero de inserciones y de borrados en mitad de la lista, utilice LinkedList.

5.

Ll comportamiento de las colas (Queue) y de las pilas se obtiene mediante LinkedList

6.

Un mapa (Map) es una forma de asociar los objetos no con valores enteros, sino con otros objeto> Los mapas HashMap estn diseados para un acceso rpido, mientras que un mapa TreeMap mantiene sus clases ordenadas y no es tan rpido como HashMap. Un mapa l.inkedHashMap mantiene sus elementos en orden de insercin. pero proporciona un acceso rpido con mecanismos de hash.

7.

Los conjuntos (Sel) slo aceptan objetos no duplicados. Los conjuntos llashSet proporcionan las bsquedas mas rpidas, mientras que TreeSel mantiene los elementos en orden LinkedHashSet mantiene los elementos en orden de insercin.

8.

No hay necesidad de utilizar las clases antiguas Vector. Hashtahlc y Staek en los nuevos programas

- Collection Genera

11 Almacenamiento de objetos 776 Map T Genera Genera I HasMap I TreeMap

ArrayList j | LinkedList | PriorityQueue J^HashSetl TreeSet J Comparable JLmkedHashSet

LinkedHashMap r Utilidades "\

Taxonoma simple de los contenedores

11 Almacenamiento de objetos 777 Como puede ver. slo hay realmente cuatro componentes contenedores bsicos Map. List. Sel y Queue, y slo dos o tres implementaciones de cada uno (las implcmentaciones de java.utiLconcurrent para Queue no estn incluidas en este diagrama). Los contenedores que ms habitualmente se utilizan son los que tienen lineas gruesas de color negro a su alrededor.

11 Almacenamiento de objetos 778

Los recuadros punteados representan interfaces, mientras que los recuadros de lnea continua son clases normales (concretas). Las lineas de puntos con flechas huecas indican que una clase concreta est implcmentando una interfaz. Las Hechas rellenas muestran que una clase puede generar objetos de la clase a la que apunta la flecha. Por ejemplo, cualquier objeto Collection puede generar un objeto Iterator, y un objeto Lis! puede generar un objeto Listlterator (adems de un objeto Iterator normal, ya que Llsl hereda de Collection)

He aqu un ejemplo que muestra la diferencia en mtodos entre las distintas clases. El cdigo concreto se ha tomado del Capitulo 15. Genricos; simplemente lo incluimos aqu para poder generar la correspondiente salida. La salida tambin muestra las interfaces que se implementan en cada clase o interfaz //: holding/ContainerMethods.java import net.mindview.util. public class Corita i nerMet hods ( public static void main(String[1 argsl { ContainerMethodDirferences.main(aras I;

) ) /* Output: (Sample) Collection: [add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, itera tor, remove, removeAll, retainAll, size, toArrayJ Interfaces in Collection: [Iterable]

11 Almacenamiento de objetos 779 Set extends Collection, adds: Interfaces in Set:[Collection] HashSet extends Set, adds: (J Interfaces in HashSet: [Set, Cloneable, Serializable] LinkedHashSet extends HashSet, addE [] Interfaces in LmkedHashSet:[Set, Cloneable, Senalizabiel TreeSet extends Set, adds: (pollLast, naviqableHeadSet, descencLinglterator, lower, headset, ceiling. pollFirst, subset, navigableTailSet. comparator, first, floor, last, navigableSubSet, higher, tailSet] Interfaces in TreeSet: [NsvigableSet, Cloneable, Serializable] List extends Collection, adds: (listIterator, mdexCf, get, sunList, set. lastlndexQf] Interfaces in List: [Collection] ArrayList extends List, adds: [ensureCapacity, tnmToSize) Interfaces in ArrayList: Serializable] [List, RandomAccess, Cloneable, offer, []

LinkedList extends List, adds: [pollLast, descendinglterator. addFirst, peefcLast, removeFirst, peekFlrst, removeLast. getLast, poll First, pop. poll, aadLast, remfrveFiistOccurrence, getFirst. element, peek. offerLaBt. push, offerrirst. removeLastOccurrence]

Interfaces in LinkedList* [List, Deque. Cloneable, Serializable] Queue extends Collection, adds; ioffer, element, peek, poll.1 Interfaces in Queue: [Collection! PriorityQueue extends Queue, adds: [comparator1 Interfaces in PriorityQueue: (Serialisable] Map: [clear, containsKey. contamsValue. entrySet, equals, get. hashCode, isEmpty, keySet. put, putAll, remove, size, values! HashMap extends Map, adds: [J Interfaces in HashMap: [Map. Cloneable, Serializable] LinkedHashMap extends HashMap, adds: [J Interfaces in LinkedHashMap: [Map] SortedMap extends Map, adds: [subMap. comparator, firstKey, laatKey, headMap, tailMap] Interfaces in SortedMap: [Map] TreeMap extends Map, adds: tdescendmyEntrySet, subMap, pollLastSntry, lastKey, floorEntry. lastEntry, lowerKey,

11 Almacenamiento de objetos 780 naviaableHeadMap, navigableTailMap, descendinaKeySet, tailMap, ceilingEntry, higherKey, pollFiratEntry, comparator, firstKey, floorKey, higherEntry, firstEntry, navigableSubMap, headMap, lcwerEntry, ceilingKey] Interfaces m TreeMap: [NavigableMap, Cloneable, Serializable]

///:*-

Como puede ver, todos los conjuntos, excepto TreeSel, tienen exactamente la misma interfaz que Collection. List y Collection difieren significativamente, aunque List requiere mtodos que se encuentran en Collection Por otro lado, los mtodos de la interfaz Queue son autnomos; los mtodos de Collection no son necesarios para crear una implementacion de Queue funcional. Finalmente, la nica interseccin entre Map y Collection es el hecho de que un mapa puede generar colecciones utilizando los mtodos entrySet( ) y values( )

Observe l interfaz de marcado ja\a.util.RandomAccess, que est asociada a ArrayList perno no a LinkedList. Esta interfaz proporciona informacin para aquellos algoritmos que quieran modificar dinmicamente su comportamiento dependiendo del USc de un objeto List concreto

Es verdad que esta organizacin parece un poco extraa en lo que respecta a las jerarquas orientadas a objetos. Sin embargo. a medida que conozca ms detalles acerca de los contenedores en java.util (en particular, en el Captulo 17, Anlisis chillado de los contenedores), vera que existen otros problemas ms importantes que esa estructura de herencia ligeramente extraa Las bibliotecas de contenedores

11 Almacenamiento de objetos 781 han constituido siempre un problema de diseo realmente difcil; resolver estos problemas implica tratar de satisfacer un conjunto de fuerzas que a menudo se oponen entre s Como consecuencia, debemos preparamos para llegar a ciertos compromisos en algunos momentos.

A pesar de estos problemas, los contenedores de Java son herramientas fundamentales que se pueden utilizar de forma cotidiana para hacer nuestros programas ms simples, mas potentes y mas efectivos. Puede que tardemos un poco en acostumbramos a algunos aspectos de la biblioteca, pero lo ms probable es que el lector comience rpidamente a adquirir y utilizar los clases que componen esta biblioteca. Puede en tonina lts olueione* a los ejercido* seleccionados en I domnenlo electrnico The Thinking M Ja\\i AnnntutfdSvluttan Guille, disponible para la venta en mi SfimtView.m

iTratamiento de errores mediante excepciones La filosofa bsica de Java es que el cdigo errneo no ser ejecutado'.

F.l momento ideal de detectar un errores en tiempo de compilacin, antes incluso de poder ejecutar el programa. Su embargo. no todos los errores pueden detectarse en tiempo de compilacin. ll resto de los problemas debern ser gestionados en tiempo de ejecucin, utilizando algn tipo de formalidad que permita que quien ha originado el error pase la informacin apropiada a un receptor que sabr cmo hacerse cargo de las dificultades apropiadamente.

Una ile las formas ms potentes de incrementar la robustez del cdigo es disponer de un mecanismo avanzado de recuperacin de errores. La recuperacin de errores es una de las preocupaciones principales de todos los programas que escribimos, pero resulta especialmente importante cu Java, donde uno de los objetivos principales consiste en crear componentes de programas para que otros los utilcen. Para crear un sistema robusto. cada componente tiene que ser robusto. Al proporcionar un modelo coherente de informe de errores utilizando excepciones. Java permite que los componentes comuniquen los problemas de manera fiable al codigo cliente.

Los objetivos del tratamiento de excepciones en Java son simplificar la creacin de programas fiables de gran envergadura utilizando menos cdigo de lo habitual y llevar esto a cabo con una mayor confianza en que nuestra aplicacin no va a encontrarse con errores no probados. F.l lema de las excepciones no resulta demasiado difcil de aprender y el mecanismo de excepciones es una de esas caractersticas que proporcionan beneficios inmediatos y de gran importancia a cualquier proyecto.

Puesto que el tratamiento de excepciones es la nica forma oficial en la que Java informa acerca de los

errores, y dado que dicho mecanismo de tratamiento de excepciones es impuesto por el compilador Java, no son demasiados los ejemplos que podramos escribir en este libro sin antes estudiar el tratamiento de excepciones En este captulo, se presenta el cdigo que es necesario escribir para gestionar las excepciones adecuadamente, mostrndose tambin cmo podemos generar nuestras propias excepciones si alguno de nuestros mtodos se mete en problemas. Conceptos

El lenguaje 0 y otros lenguajes anteriores disponan a menudo de mltiples esquemas de tratamiento de errores, y dichos esquemas se solan establecer por convenio y no como pane del lenguaje de programacin. Normalmente, lo que se haca era devolver un valor especial o configurar una variable indicadora, y se supona que el receptor deba examinar el valor o la variable > determinar que algo iba mal Sin embargo, a medida que fueron pasando los aos, se descubri que los programadores que utilizaban una biblioteca tendan a pensar en si mismos como si fueran inmunes al error: era casi como si dijeran "Si. puede que a otros se les presenten errores, pero en mi axJtgo no hay errores. Por tamo, de forma bastante natural, los programadores tendan a no comprobar las condiciones del error (y adems, en ocasiones, esas condiciones de error eran demasiado tontas como para comprobarlas1) Si furamos tan exhaustivos como para comprobar si se ha producido un error cada vez que invocamos un mtodo, el cdigo se convertira en una pesadilla ilegible. Puesto que los programadores 1-1 programador en t puede, por ejemplo, consultai el valor de tviorno de printfO

podian, a pesar de iodo, construir sus sistemas con estos lenguajes, se resistan a admitir la realidad: que esta tcnica de ges- tin de errores representaba una limitacin importante a la hora de crear programas de gran envergadura que fueran robus- tos y mantcnibles.

La solucin consiste en eliminar la naturaleza casual del tratamiento de errores e imponer una cierta formalidad. Este modo de proceder tiene, en la prctica, una larga historia, porque las tmplementaciones de los mecanismos del tratamiento de excepciones se remontan a los sistemas operativos de la dcada de 1960, e incluso a la instruccin "on error goto" de BASIC Pero el mecanismo de tratamiento de excepciones de C++ estaba basado en Ada. y el de Java se basa principalmente en C-H- (aunque se asemeja ms al de Object Pascal).

La palabra excepcin hace referencia a algo que no tiene lugar de la forma acostumbrada. En el lugar donde aparece un problema, puede que no sepamos qu hacer con l. pero de lo que si podemos estar seguros es de que no podemos continuar como si no hubiera pasado nada; es necesario pararse y alguien, en algn lugar, tiene que saber cmo responder al error. Sin embargo, no disponemos de suficiente informacin en el contexto actual como para poder corregir el problema, asi que lo que hacemos es pasar dicho problema a otro contexto superior en el que haya alguien cualificado para tomar la decisin adecuada.

El otro beneficio importante derivado de las excepciones es que tienden a reducir la complejidad del cdigo de gestin de errores. Sin las excepciones, es necesario comprobar si se ha producido un error concreto y resolverlo en mltiples lugares del programa. Sin embargo, con las excepciones ya no hace falta comprobar los errores en el punto donde se produce la llamada a un mtodo, ya que la excepcin garantiza que alguien detecte el error Adems, slo hace falta tratar el problema en un nico sitio, en lo que se denomina la ratina de tratamiento de excepciones Esto nos ahorra cdigo y permite tambin separar el cdigo que describe lo que queremos hacer durante la ejecucin normal, de ese otro cdigo que se ejecuta cuando las cosas van mal. En general, la lectura, la esentura y la depuracin del cdigo se hacen mucho ms claras con las excepciones que cuando se utiliza la antigua forma de tratar los errores.

Excepciones bsicas

Una condicin excepcional es un problema que impide la continuacin del mtodo o mbito actuales. Resulta importante distinguir las condiciones excepcionales de los problemas normales, en los que disponemos de la suficiente informacin dentro del contexto actual, como para poder resolver de alguna manera la dificultad con que nos hayamos encontrado. Con una condicin excepcional, no podemos continuar con el procesamiento, porque no disponemos de la informacin necesaria para tratar con el problema en el contexto actual. Lo nico que podemos hacer es saltar fuera del contexto actual y pasar dicho problema a otro contexto de orden superior Esto es lo que sucede cuando generamos una excepcin.

La divisin representa un ejemplo sencillo: si estamos a punto de dividir por cero, merece la pena comprobar dicha condicin. pero que implica que el denominador sea cero? Puede que sepamos, en el contexto del problema que estemos intentando resolver en ese mtodo concreto, cmo tratar con un denominador cero. Pero si se trata de un valor inesperado, no podremos tratar con l. y deberemos por tanto generar una excepcin en lugar de continuar con la ruta de ejecucin en la que estuviramos

Cuando generamos una excepcin suceden varias cosas. En primer lugar, se crea el objeto excepcin de la misma forma que cualquier otro objeto Java: en el cmulo utilizando la instruccin new. A continuacin, se detiene la ruta actual de ejecucin (aquella que ya no podemos continuar) y se extrae del contexto actual la referencia al objeto excepcin. En este punto, el mecanismo de tratamiento de excepciones se hace cargo del problema y comienza a buscar un lugar apropiado donde continuar ejecutando el programa. Dicho lugar apropiado es la rutina de tratamiento de excepciones, cuya tarea consiste en recuperarse del problema de modo que el programa pueda intentar hacer otra cosa o simplemente continuar con lo que estuviera haciendo.

Como ejemplo simple de generacin de excepciones vamos a considerar una referencia a un objeto denominada t Es posible que nos pasen una referencia que no haya sido inicializada, asi que podramos tratar de comprobarlo antes de invocar un mtodo en el que se utilice dicha referencia al objeto. Podemos enviar la informacin acerca del error a otro contexto de orden superior, creando un objeto que represente dicha informacin y extrayndolo del contexto acta!. Este proceso se denomina generar una excepcin. He aqu el aspecto que tendra en el cdigo: i(t == nuil) throw new NullPointerException(l;

Esto enera la excepcin, lo que nos permite, en el contexto actual, olvidamos del problema: dicho problema ser gestionado de manera transparente en algn otro lugar. En breve veremos dnde exactamente se gestiona la excepcin.

Las excepciones nos permiten pensar en cada cosa que hagamos como si se tratara de una transaccin, encargndose las excepciones de proteger esas transacciones: "...la premisa fundamental de las transacciones es que hacia falta un mecanismo de tratamiento de excepciones en la informtica distribuida. Las transacciones son el equivalente informtico de los contratos legales. Si algo va mal. detenemos lodos los clculos2. Tambin podemos pensar en las transacciones como si se tratara de un sistema integrado para deshacer acciones, porque (con un cieno cuidado) podemos establecer varios puntos de recuperacin en cualquier programa. Si una parte del programa falla, la excepcin se encarga de deshacer" las operaciones hasta un punto estable conocido dentro del programa.

Uno de los aspectos ms importantes de las excepciones es. que si sucede algo realmente grave, no permiten que el programa contine con la ruta de ejecucin ordinaria. Este tema ha constituido un grave problema en lenguajes como C y C++; especialmente en C, donde no habia ninguna forma de impedir que el programa continuara por una ruta de ejecucin en caso de aparecer un problema, de modo que era posible ignorar los problemas durante un largo tiempo y acabar en un estado totalmente inadecuado Las excepciones nos permiten (entre otras cosas) obligar al programa a detenerse y a informamos de qu es lo que ha pasado o. idealmente, obligar al programa a resolver el problema y volver a un estado estable.

Argumentos de las excepciones

Al igual que sucede con cualquier objeto en Java, las excepciones siempre se crean en el cmulo de memoria utilizando ne, lo que asigna el correspondiente espacio de almacenamiento c invoca un constructor. Existen dos constructores en todas las excepciones estndar El primero es el constnictor predeterminado V el segundo toma un argumento de cadena de caracteres para poder aportar la informacin pertinente a la excepcin: throw new NullPointerExceptionf"t * nuil);

Esta cadena de caracteres puede posteriormente extraerse utilizando vanos mtodos, como tendremos oportunidad de ver.

La palabra clave throw produce una serie de resultados interesantes. Despus de crear un objeto excepcin mediante new. le proporcionamos la referencia resultante a throw En la prctica, el objeto es "devuelto desde el mtodo, aun cuando dicho tipo de objeto no es normalmente lo que estaba diseado que el mtodo devolviera. Una forma bastante simplificado- ra de considerar el mecanismo de tratamiento de excepciones es como si fuera un tipo distinto de mecanismo de retomo, aunque no debemos caer en la tentacin de llevar esa analoga demasiado lejos, porque podramos metemos en problemas Tambin podemos salir de mbitos de ejecucin ordinarios generando una excepcin. En cualquiera de los casos, se devuelve un objeto excepcin y se sale del mtodo o mbito donde la excepcin se haya producido.

Las similitudes con el proceso nomial de devolucin de resultados por pane de un mtodo terminan aqu, porque el lugar al que se vuelve es completamente distinto de aquel al que volvemos en una llamada normal a un mtodo (volvemos a una rutina apropiada de tratamiento de excepciones que puede

estar alejada muchos niveles dentro de la pila del lugar en el que se ha generado la excepcin).

Adems, podemos generar cualquier tipo de objeto Throwable, que es la clase raiz de las excepciones. Normalmente, lo que haremos ser generar una clase de excepcin distinta para cada tipo diferente de error. La informacin acerca del error est representada tanto dentro del objeto excepcin como, implcitamente, en el nombre de la clase de excepcin, por lo que alguien en el contexto de orden superior puede detenninar qu es lo que hay que hacer con la excepcin (a menudo, la nica informacin es el tipo de excepcin, no almacenndose ninguna informacin significativa dentro del objeto excepcin). Deteccin de una excepcin

Para ver cmo se detecta una excepcin, primero es necesario entender el concepto de regin protegida. Se trata de una seccin de cdigo que puede generar excepciones que est seguida por el cdigo necesario para tratar dichas excepciones. ' Jim Cira y. ganador del premio Turint* Award por las contribuciones que su equipo ha rea!iado al tema de la* transacciones La> palabras estn tomadas de nnu entrevista publicada en ten m triquen? irrx El bloque try

Si nos encontramos dentro de un mtodo y generamos una excepcin (o si otro mtodo al que invoquemos dentro de ste genera una excepcin), dicho mtodo terminar al generarse la excepcin Si no queremos generar (con throw) la excepcin para salir del mtodo, podemos definir un bloque especial dentro de dicho mtodo para capturar la excepcin. Este hlo- que se denomina bloque try (probar) porque lo que hacemos en la prctica es probar" dentro de esc bloque las diversas llamadas a mtodos. El bloque try es un mbito de ejecucin ordinario precedido por la palabra clave try try { // Cdigo que podra generar excepciones

Si quisiramos comprobar cuidadosamente los errores en un lenguaje de programacin que no tuviera mecanismos de tratamiento de excepciones, tendramos que rodear todas las llamadas a mtodos con cdigo de preparacin y de comprobacin de errores, incluso si invocramos el mismo mtodo en varas ocasiones. Con el tratamiento de excepciones incluimos todo dentro del bloque try y capturamos todas las excepciones en un nico lugar. Esto significa que el cdigo es mucho ms fcil de escribir y de leer, porque el objetivo del cdigo no se ve confundido con los mecanismos de comprobacin de errores. Rutinas de tratamiento de excepciones

Por supuesto, la excepcin generada debe acabar siendo tratada en algn lugar. Dicho lugar" es la rutina Je tratamiento de excepciones, existiendo una de dichas rutinas por cada tipo de excepcin que queramos capturar. Las rutinas de tratamiento de excepciones estn situadas inmediatamente a continuacin del bloque try y se denotan mediante la palabra clave catch try { // Cdigo que podra generar excepciones ) catch(Tipal idl) ( // Tratamiento de las excepciones de Tipo! ) catch(Tipo2 id2) ( // Tratamiento de las excepciones de Tipo2 | catch(Tipo3 id3) ( i // Tratamiento de las excepciones de Tit>c3

// etc...

Cada clusula catch (rutina de tratamiento de excepciones) es como un pequeo mtodo que toma un nico argumento de un tipo concreto. Ll ulentificador (idl. id2. etc.) puede utilizarse dentro de la rutina

de tratamiento de excepciones exactamente igual que un argumento de un mtodo En ocasiones, nunca utilizamos el identificador. porque el tipo de la excepcin nos proporciona ya suficiente informacin como para poder tratar con ella, pero a pesar de todo el identificador debe estar presente.

Las rutinas de tratamiento de excepciones deben aparecer inmediatamente despus del bloque try Si se genera una excepcin. el mecanismo de tratamiento de excepciones trata de buscar la primera rutina de tratamienio cuyo argumento se ajus- le al tipo de la excepcin. A continuacin, entra en la clusula catch. y la excepcin se considera tratada, l a bsqueda de rutinas de tratamiento se detiene en cuanto finalizada la clusula catch. Slo se ejecutara la clusula catch que se ajuste al tipo de la excepcin; estas clusulas no son como las instrucciones switch. en las que hace falta una instruccin break despus de cada clusula case para impedir que las restantes clusulas se ejecuten

Observe que. dentro del bloque try. puede haber distintas llamadas a mtodos que generen la misma excepcin, pero slo hace falta una nica rutina de tratamiento. Terminacin y reanudacin

Existen dos modelos bsicos en la teora de tratamiento de excepciones. Java sopona el modelo denominado de termina- i ion* en el que asumimos que el cnor es tan critico que no hay forma de volver al lugar en el que se gener la excepcin. Como b mnyoriH de U>s lenguajes, incluyendo C ++. C. Pylhon, D. etc.

Quienquiera que generara la excepcin, decidi que no haba ninguna manera de salvar la situacin, por lo que no desea que volvamos a ese punto en el que la excepcin fue generada.

La alternativa se denomina reanudacin. F.sta alternativa implica que la rutina de tratamiento de excepciones debe hacer algo para rectificar la situacin, despus de lo cual se vuelve a intentar ejecutar el mtodo fallido, presumiendo que esa segunda ve/ tendr xito. Si queremos utili/ar la tcnica de reanudacin, quiere decir que esperamos podemos continuar con la ejecucin despus de que la excepcin sea tratada

Si quiere disponer de un comportamiento de reanudacin en Java, no genere una excepcin cuando se encuentre con error En lugar de ello, invoque un mtodo que corrija el problema. Alternativamente, coloque el bloque try dentro de un bucle wblle que contine volviendo a entrar en el bloque try hasta que el resultado sea satisfactorio.

Histricamente, los programadores que utilizaban sistemas operativos donde se admita el tratamiento de excepciones con reanudacin terminaron utilizando codigo basado en el mecanismo de terminacin y evitando emplear la reanudacin. Por tanto, aunque la reanudacin pueda parecer atractiva a primera vista, no resulta demasiado til en la practica. La razn principal es, probablemente, el acoplamiento resultante: las rutinas de tratamiento con reanudacin necesitan saber dnde se ha generado la excepcin, y necesitan tambin contener cdigo no genrico especfico de cada ubicacin donde la excepcin se genere. Esto hace que el cdigo sea difcil de escribir y de mantener, especialmente en sistemas grandes en los que las excepciones puedan generarse en muchos puntos distintos. Creacin de nuestras propias excepciones

No tenemos porque limitamos a utilizar las excepciones Java existentes. La jerarqua de excepciones de Java no puede prever todos los errores de los que vayamos a querer informar, asi que podemos crear nuestros propios errores para indicar un problema especial con el que nuestra biblioteca pueda encontrarse.

Para crear nuestra propia clase de excepcin, debemos heredar de una clase de excepcin existente, preferiblemente de una cuyo significado este prximo al de nuestra propia excepcin (aunque a menudo esto no es posible). La forma ms trivial de crear un nuevo tipo de excepcin consiste, simplemente, en permitir que el compilador cree el constructor predeterminado por nosotros, por lo que no hace falta prcticamente ningn cdigo: // : exceptions/InhentmgExceptions. java // Creacin de nuestras propias excepciones. class SlmpleException extends Exception () public class InneritmgExceptions { public void f t) throws SlmpleException { System.out.println("Throw SlmpleException from throw new SlmpleException();

public static void manStringU args ( InhentmgExceptions sed = new InheritingExceptions()j try | sed. () ; ) catch(SimpleException e) ( ) System.out.printlnl"Caught t!"J;

l / Output: Throw SlmpleException from f{) Caught it!

*///:-

El compilador crea un constructor predeterminado, que de forma automtica (e invisible) invoca al constructor predeterminado de la clase base Por supuesto, en este caso no obtenemos un constructor SimplcException(Strng), pero en la prctica dicho constructor no se usa demasiado. Como veremos, lo

ms importante acerca de una excepcin es el nombre de la clase, por lo que la mayor pane de las veces una excepcin como la que aqu se muestra ser perfectamente adecuada.

Aqu, el resultado se imprime en la consola, donde se captura y se comprueba automticamente utilizando el sistema de visual ilacin de la salida de programas de este libro. Sin embargo, tambin podramos enviar la informacin de error a la salida de error estndar escribiendo en System.crr. Normalmente, suele ser mejor enviar aqu la informacin de error que . System.out. que puede estar redirigido. Si se enva la valida ;i Syslcm.err. no ser redirigida junto con Syslem.out. por lo que es ms probable que el usuario vea la informacin.

Tambin podemos crear una clase de excepcin que tenga un constructor con un argumento String; f f : exceptions/FullConstructors.java class MyException extends Exception ( public MyException<) (} public MyException(Strina msg) ( superimsgl; ) J public clas3 FullConstructors { public static void f<) throws MyException { System.out.printIni"Throwing MyException from fil"); throw new MyException() t

public static void g() throws MyException { System.out.println("Throwing MyException from g l l ; throw new MyExceptlon(MOriginated in g()*);

] public static void main/String[J

args) { try ( it); } catch(MyException el { e printStackTrace(System.out);

} try ( 9 ill } catch{MyException el ( e.printStackTrac*(System.out)t )

) ) / Output Throwing MyException from f(} MyExcept i on at Ful iconst rue tora. f {Ful 1 Construe tors. java *. 11) at FuilCottflaructoxs .main (Ful iConstructars .}ava:19) Throwing MyException from g(j MyExceptionr Originated tn gl) at FullConstruetors.g(FullConstructors.j ava:151 at FullConstructors main IFullConstructors.java:24)

///!-

I cdigo aadido es de pequeo tamao; dos constructores que definen la forma en que se crea MyException En el segundo constructor, se invoca explcitamente el constructor de la clase base con un argumento String utilizando la palabra clavc super
I

n las rutinas de tratamiento, se invoca uno de los mtodos Throwable (de donde se hereda Exception): printStackTracet ) Como puede ver a la salida, esto genera informacin acerca de la secuencia de mtodos que fueron invocados hasta llegar al punto en que se gener la excepcin. Aqu, la informacin se enva a S>stem.out. siendo automticamente capturada y mostrada a la salida. Sin embargo, si invocamos la versin predeterminada:
i

e.printStarrkTrace t) ;

la informacin va a la salida de error estndar.

Ejercicio 1: (2) Cree una clase con un mtodo main( ) que genere un objeto de la clase Exception dentro de un blo

que ir> Proporcione ul constructor de Exceptiun un argumento String. Capture la excepcin dentro de una clusula catch e imprima el argumento String. Aada una clusula fnally e imprima un mensaje para demostrar que pas por all.

Ejercicio 2: (I) Defina una referencia a un objeto e inicialicela con nuil Trate de invocar un mtodo a travs de esta

referencia. Ahora rodee el cdigo con una clusula trv-catch para capturar la excepcin.

Ejercicio 3: (I) Escriba cdigo para ArraylndcxOutOfBoundsExeeption i ndice de

generar

capturar

una

excepcin

matriz fuera de limites).

Ejercicio 4: (2) Cree su propia clase de excepcin utilizando la palabra clave extends. Escriba un constructor para

dicha clase que tome un argumento String y lo almacene dentro del objeto como una referencia de tipo String. Escriba un mtodo que muestre la cadena de caracteres almacenada. Cree una clusula try-catch para probar la nueva excepcin.

Ejercicio 5: (3) Defina un comportamiento de tipo reanudacin utilizando un bucle while que se repita hasta que se

deje de generar una excepcin. Excepciones y registro

Tambin podemos registrar la salida utilizando java.util.logging. Aunque los detalles completos acerca de los mecanismos de registro se presentan en el suplemento que puede encontrarse en la direccin http./ZMimil uw.rtet/Books/BelterJuva, los mecanismos de registro bsicos son lo suficientemente sencillos como para poder utilizarlos aqui. //: exceptione/LoggmgExceptions. java // Una excepcin que proporciona la informacin a travs de un registro, import java.util.logging.; import java.io.*, class LoggingException extends Exception ( private static Logger logger * Logger .get:Logger ("LoggmgExcepcion*4) ; public LoagingExceptionO ( Strir.gWriter trace new StringWriter () ,* printStackTrace {new PrintWncer (trace) ) ; logaer.severe(trace.toString() );

) public class LogglngExceptions { public static void main(String(] args' ( try { throw new LoggingException(); ) catchiLoggingException e) ( System.err.println{"Caught " e) ;

) try { throw new LoggingException() ; } catch(LoggingException e) { System err.printIn("Caught " e);

}
I

/ Output: (85% match)

Aug 30, 2005 4:02:31 PM LoggingException <init> SEVERE: LoggingException at LogoingExceptions.main(LoggingExcept ions.j ava:19 J Caught LoggingException Aug 30, 2005 4:02:31 PM LoggmgExcepcin <init> SEVERE: Loag mgExcept ion

at Lcgg mgExcept ions. mam (Loga mgExcept i ons .java: 24) Caught LoggmgException ///?-

ti mtodo esttico Logger.getLogger( ) crea un objeto Logger asociado con el argumento String (usualmente. el nombre del paquete y la clase a la que se refieren los errores) que enva su salida a System.err. La forma ms fcil de escribir en un objeto Logger consiste simplemente en invocar el mtodo asociado con el nivel de mensaje de registro; aqu utilizamos severe( ) Para producir el objeto String para el mensaje de registro, convendra disponer de la traza de la pila correspondiente al lugar donde se gener la excepcin, pero printStackTrace ) no genera un objeto String de forma predeterminada. Para obtener un objeto String. necesitamos usar el mtodo sobrecargado printStackTrace( ) que toma un objeto javs.io.PrintW riter como argumento (todo esto se explicar con detalle en el Capitulo 18. E S ) . Si entregamos al constructor de PrintWritcr un objeto java.io.StringW riter. la salida puede extraerse como un objeto String invocando toString()

Aunque la tcnica utilizada por LoggmgException resulta muy cmoda, porque integra toda la infraestructura de registro dentro de la propia excepcin, y funciona por tanto automticamente sin intervencin del programador de clientes, lo ms comn es que nos veamos en la situacin de capturar y registrar la excepcin generada por algn otro, por lo que es necesario generar el mensaje de registro en la propia rutina de tratamiento de excepciones: //: except ions/LoggmgExceptions2 . java // Registro de las excepciones capturadas, import java.til.logging. import java.lo.*: pubiic class LoggingExceptians2 ( prvate static Logger loggex * Logger.geCLogger("LogaingExcepti ons2"); static vod logException(Exception e) { StringWriter trace = new StringWriter < > ,* e.printStackTracelnew PrIntWriter trace)); logger .severe(trace.toStringO ) ;

pubiic static void mair. (String U args) ( try ( throw new NtillPo nter Except ion () ,*

cacch (NullPomterException e) ( logException le)/


I

> / Outputi (90% match) Aug 30, 2005 4:07:54 PM LogaingExceptions2 logException SEVERE: Java.iang.NullFainterException at LoggmgExceptions2 .mam (Logo mgExcept i ons2 .java: 16)

*///:-

F1 proceso de creacin de nuestras propias excepciones puede llevarse un paso ms all. Podemos aadir constructores N miembros adicionales: //: exceptons/ExtraFeaturea.java // Mejora de las clases de excepcin, import static net.mindview.util.Print.*; class MyExceptian2 extends Excepcin { private int xj pubiic MyException2() ()

pubiic MyException2(String msg) ( superlmsgj; ( pubiic MyException2(String msg, int x) (

this.x = x; i 12 Tratamiento de errores mediante excepciones 802 3uper(msg); public int val) ( return x; ) public String getMessage{) ( return Detail Message: x + * **+ super .getMessage () ;

} public class ExtraFeatures | public static void ft) throws MyException2 { print("Throwing MyException2 from f I)M)* throw new MyExceotion2O ; i public static void g I) throws MyException2 ( print ("Throwing MyException2 from gM")/ throw new MyException2("Originated in g()");

] public static void hfl throws MyException2 { print("Throwing MyException2 from h()M); throw new MyException2("Originated in h(>". 47);

this.x = x; i 12 Tratamiento de errores mediante excepciones 803 ) public static void main(String[] args) ( try ( ft); ) catch(Myxception2 e) { e.printStackTrace(System.out);

) try ( gO ; ) catch(MyExceptionS ej ( e.printStackTrace(System.out);

) try (

h() f ) catchMyException2 e) { e.printStackTrace(System.out I; System.out.printIn("e-val() = " + e.val(J);

this.x = x; i 12 Tratamiento de errores mediante excepciones 804 }

> | / Output: Throwing MyException2 from ft) MyException2! Detail Message: 0 null at ExtraFeatures.fiExtraFeatures.jav a:22) at ExtraFeatures.main<ExtraFeatures. java:34) Throwing MyException2 from g() MyException2: Detail Message: 0 Originated in gl) at ExtraFeatures.g(ExtraFeatures. java26) at ExtraFeatures.main<ExtraFeatur es.java:39) Throwing MyException2 from h() MyException2: Detail Message: 47 Originated in h() at ExtraFeatures.h(ExtraFeatures.j ava:301 at ExtraFeatures.main(ExtraFeatures.java: 44) e.vald - 47

*///:

this.x = x; i 12 Tratamiento de errores mediante excepciones 805 Sc ha aadido un campo x junto con un mtodo que Ice dicho valor v un constructor adicional que lo inicializa. Adems,

I hro\vable.get!Vlcssage<) ha sido sustituido para generar un mensaje de detalle ms interesante. get.Mcssage() es un mtodo similar a toString() que se utiliza para las clases de excepcin.

Puesto que una excepcin no es ms que otro tipo de objeto, podemos continuar este proceso de mejora de nuestras clases de extensin. Recuerde, sin embargo, que todo este trabajo adicional puede no servir para nada en los programas de cliente que utilicen nuestros paquetes, ya que dichos programas puede que simplemente miren cual es la excepcin que se ha generado y nada ms (sa es la forma en la que se utilizan la mayora de las excepciones de la biblioteca Java).

Ejercicio 6: (I) Cree dos clases de excepcin, cada una de las cuales realice su propia tarea de registro automticamen

te. Demuestre que dichas clases funcionan.

this.x = x; i 12 Tratamiento de errores mediante excepciones 806 Ejercicio 7: < 1) Modifique el Ejercicio 3 para que la clusula catch registre los resultados.

La especificacin de la excepcin

En Java, debemos tratar de informar al programador de clientes, que invoque nuestros mtodos, acerca de las excepciones que puedan ser generadas por cada mtodo. Esto resulta conveniente porque quien realiza la invocacin puede saber asi exactamente qu cdigo necesita escribir para capturar todas las excepciones potenciales. Por supuesto, si el cdigo fuente est disponible, el programador de clientes podra examinarlo y buscar las instrucciones hrow. pero puede que una biblioteca se distribuya sin el correspondiente cdigo fuente. Para evitar que esto constituya un problema. Java proporciona una sintaxis (y nos obliga a usar esa sintaxis) para permitimos informar al programador de clientes acerca de cules son la* excepciones que el mtodo genera, de modo que el programador pueda tratarlas. Se trata de la especificacin de excepciones que forma parte de la declaracin del mtodo y que aparece despus de la lista de argumentos

La especificacin de excepciones utiliza una palabra clave adicional, throws. seguida de todos los tipos potenciales de excepciones. Por tanto, la definicin de un mtodo podra tener el aspecto siguiente: void f() throws TooBig, TooSmall, DivZero ( //,..

Sin embargo, si decimos void f 0 { // ...

this.x = x; i 12 Tratamiento de errores mediante excepciones 807 significa que el mtodo no genera ninguna excepcin (salvo por las excepciones heredadas de KuntimeException. que pueden generarse en cualquier lugar sin necesidad de que exista una especificacin de excepciones; hablaremos de estas excepciones ms adelante).

No podemos proporcionar informacin falsa acerca de la especificacin de excepciones. Si el cdigo dentro del mtodo provoca excepciones y ese mtodo no las trata, el compilador lo detectar y nos informara de que debemos tratar la excepcin o indicar, mediante una especificacin de excepcin, que dicha excepcin puede ser devuelta por el mtodo Al imponer que se utilicen especificaciones de excepcin en todos los lugares. Java garantiza en tiempo de compilacin que exista una cierta correccin en la definicin de las excepciones.

Slo hay una manera de que podamos proporcionar informacin falsa: podemos declarar que generamos una excepcin que en realidad no vayamos a generar. El compilador aceptar lo que le digamos y forzar a los usuarios de nuestro mtodo a tratar esa excepcin como si realmente fuera a ser generada. La nica ventaja de hacer esto es disponer por adelantado de una forma de aadir posteriormente la excepcin; si ms adelante decidimos comenzar a generar esa excepcin, no tendremos que realizar cambios en el cdigo existente. Tambin resulta importante esta caracterstica para crear interfaces y clases bases abstractas cuyas clases derivadas o implcmentaciones puedan necesitar generar excepciones.

Las excepciones que se comprueban y se imponen en tiempo de compilacin se denominan excepciones comprobadas.

this.x = x; i 12 Tratamiento de errores mediante excepciones 808 Ejercicio 8: (I) Escriba una clase con un mtodo que genere una excepcin del tipo creado en el Ejercicio 4. Trate de

compilarlo sin incluir una especificacin de excepcin para ver qu es lo que dice el compilador. Aada la especificacin de excepcin apropiada. Pruebe la clase y su correspondiente excepcin dentro de una clusula trv-catch Cmo capturar una excepcin

Resulta posible crear una rutina de tratamiento que capture cualquier tipo de excepcin. Para hacer esto, podemos capturar el upo de excepcin de la clase base Exception (existen otros tipo de excepciones base, pero Exception es la base que resulta apropiada para casi todas las actividades de programacin): catch(Excepcin e) ( System.ouc.princln("Caughc an excepcin"}?

Esto permitir capturar cualquier excepcin, por lo que si usamos este mecanismo convendr ponerlo al final de la lista de rutinas de tratamiento, con el fin de evitar que se impida entrar en accin a otras rutinas de tratamiento de excepciones que puedan estar situadas a continuacin.

this.x = x; i 12 Tratamiento de errores mediante excepciones 809 Puesto que la clase Exception es la base de todas las clases de excepcin que tienen importancia para el programador, no vamos a obtener demasiada informacin especifica acerca de la excepcin concreta que hayamos capturado, pero podemos invocar los mtodos incluidos en su tipo base Throwable: Strin getMessage()

Strinj getLocalizcdMcssage()

Obtiene el mensaje de detalle o un mensaje ajustado para la configuracin de idioma utilizada.

String toString()

Devuelve una descripcin corta del objeto Throwable. incluyendo el mensaje de detalle, si es que existe uno.

this.x = x; i 12 Tratamiento de errores mediante excepciones 810 vnid printStaek'Traee( )

void printStackTraee(PrintStrearo)

void prntStackTrace(java.io.Print\Vriter)

Imprime el objeto Throwable y la traza de pila de llamadas de dicho objeto. La pila de llamadas muestra la secuencia de llamadas a mtodos que nos han conducido hasta el lugar donde la excepcin fue generada. La primera versin imprime en la salida de error estndar, mientras que la segunda y la tercera imprimen en cualquier salida que elijamos (en el Capitulo i8. F/S, veremos por qu existen dos tipos de flujos de salida). Throwable filllnStack Irace()

Registra informacin dentro de este objeto Throwable acerca del estado actual de los marcos de la pila. Resulta til cuando una aplicacin est volviendo a generar un error o una excepcin (hablaremos de esto en breve).

this.x = x; i 12 Tratamiento de errores mediante excepciones 811 Adems, tambin tenemos acceso a otros mtodos del tipo base de Throwable que es Object (que es el tipo base de todos los objetos). El que ms til puede resultar para las excepciones es getClass( ), que devuelve un objeto que representa la clase de este objeto. A su vez, podemos consultar este objeto Class para obtener su nombre mediante getName( ). que incluye informacin o getSimpleName(). que slo devuelve el nombre de la clase.

He aqui un ejemplo que muestra el uso de los mtodos bsicos de Exeepiion //: exceptions/BxcepcionMechods.java // IlusCracin de los mcodos de Excepcin, import static nec .mindview.ucil.princ . .pubiic class ExceptionMethods { pubiic scacic void main (String [] args) { try f throw new Excepcinl"My Excepcin);

} cacch(Excepcin e) { princ("Caughc Excepcin); princ ("gecMessage () : " + e.gecMessage()); prinC("gecLocalizedMessage():" + e.gecLocalizedMessage()>; pnnt ("toString () : * + e); prinC{"prinCSCackTraceO :M) ; e.princScackTrace(SysCem.ouC);

this.x = x; i 12 Tratamiento de errores mediante excepciones 812 ) ) /* Output: Caughc Excepcin gecMessageO:My Excepcin gecLocalizedMessage():My Excepcin C0SCring():java.lana.Excepcion: My Excepcin prlntStackTrace(): j ava. i ana. Except ion: My Except i on at ExceptionMethods.raain(ExceptionMethods.java:0)

///:-

Podemos ver que los mtodos proporcionan sucesivamente mas informacin, de hecho, cada uno de ellos es un supercon- junto del anterior.

Ejercicio 9: las tres n

(2) Cree tres nuevos lipos de excepciones. Escriba una clase con un mtodo que genera

this.x = x; i 12 Tratamiento de errores mediante excepciones 813 main( ). llame al mtodo pero utilice una nica clusula catch que permita capturar los tres tipos de excepciones. La traza de la pila

Tambin puede accedente directamente a la informacin proporcionada por printStackTracd ). utilizando gctStack l race( ) Este mtodo devuelve una matriz de elementos de traza de la pila, cada uno de los cuales representa un murco de pila. El elemento cero es la parte superior de la pila y se corresponde con la ltima invocacin de mtodo de la secuencia (el punto en que este objeto Throw able fue generado). El ltimo elemento de la matriz ( la pane inferior de la pila) es la primera invocacin de mtodo de la secuencia. Este programa proporciona una ilustracin simple //: exceptions/WhoCalled.java // Acceso mediante programa a la informacin de traza de la pila. public class WhoCalled ( static void f() ( // Generar una excepcin para rellenar la traza de pila, try ( throw new Exception O ; ) catch (Exception e) ( for(StackTraceElement ste . e.getStackTrace(}> System.out.printIn(ste.getWethodName<));

this.x = x; i 12 Tratamiento de errores mediante excepciones 814 static void g() ( fllr ) 6tatic void h() ( gU; ) public static void main (String {,] args> { f O; System.out .printIni" - - ------------* I ;

g () ; System, out. print In ("-------- ----fall; " ] -

) ) /* Output: f main f

g ma in f

this.x = x; i 12 Tratamiento de errores mediante excepciones 815 g h main

///:-

Aqui. nos limitamos a imprimir el nombre del mtodo, pero tambin podramos imprimir el objeto completo StackTraceElement. que contiene informacin adicional Regeneracin de una excepcin

En ocasiones, conviene regenerar una excepcin que hayamos capturado, particularmente cuando se utili/a Exception para capturar todas las excepciones. Puesto que ya disponemos de la referencia a la excepcin actual, podemos simplemente volver a generar dicha referencia: catch(Exception e) ( System.out.printIR("An exception was thrown"); throw s; I

La regeneracin de una excepcin hace que esta pase a las rutinas de tratamiento de excepciones del

this.x = x; i 12 Tratamiento de errores mediante excepciones 816 siguiente contexto de nivel superior. Las clusulas catch adicionales incluidas en el mismo bloque try seguirn ignorndose. Adems, se preserva toda la informacin acerca del objeto de excepcin, por lo que la rutina de tratamiento en el contexto de nivel superior que capture ese tipo excepcin especifico podr extraer toda la informacin de dicho objeto.

Si nos limitamos a regenerar la excepcin actual, la informacin que imprimamos acerca de dicha excepcin en printStackTrace() ser la relativa al origen de la excepcin, no la relativa al lugar donde la hayamos regenerado. Si queremos insertar nueva informacin de traza de la pila podemos hacerlo invocando filllnStackTrace( ). que devuelve un objeto rhrowable que habr generado insertando la informacin de pila actual en el antiguo objeto de excepcin. He aqu un ejemplo: f/: exceptions/Rethrowing.java // Ilustracin de fllllnStackTrace1 1 public class Rethrowing { public static void () throws Exception { System, out .print in ("originating the exception in f()*Mj throw new Exception I"thrown from 0");

) public static void gtJ throws Exception ( try { f l l i ) catch(Exception e) { System, out. print In 1 "Inside gO ,e.printStackTrace () ") ; e.print3tackTrace(System.out); throw ej )

puhlic starir. void hi) throws F.xc^prlrm { try {

this.x = x; i 12 Tratamiento de errores mediante excepciones 817 U); ) catchiExcept ion e j System.out.printin("Inside hi)e.printStackTrace 0"); e.printStackTrace <: System, out) ; throw (Exception)e.filllnStackTraceil;

public static void main(Stringfl argsi { try { g() ; ) catch'Exception e\ j System.out.printIn("main: printStackTracei "); e.printStackTrace(System.ouc); 1 try | h ( ) ; | catch(Exception e> ( System, out. print In ("main: printStackTrace0 ' > ; e.printStackTrace(System.out};

) ) /* Output: originating the exception in f{) Inside g(), e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:7) at Rethrowing.g(Rethrowing.java:11) at Rethrowing.main(Rethrowing.java:29 ) main: printStackTrace{)

this.x = x; i 12 Tratamiento de errores mediante excepciones 818 java.lang.Exception: thrown from f{) at Rethrowing.f(Rethrowing.java:7) at Rethrowing.giRethrowing.j ava:11) at Rethrowing.main(Rethrowing.java;29 ) originating the exception in ff) Inside h(),e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:7) at Rethrowing.h(Rethrowing.java:20) at Rethrowing.main(Rethrowing.java:35 ) main: printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.hRethrowing.java :24) at Rethrowing.main(Rethrowing.j ava:35)

*///:-

La linea donde se invoca fillInStackTrace() ser el nuevo punto de origen de la excepcin.

Tambin resulta posible volver a generar una excepcin distinta de aquella que hayamos capturado. Si hacemos esto, obtenemos un efecto similar a cuando usamos fillInStackTracc(): la informacin acerca del lugar de origen de la excepcin se pierde, y lo que nos queda es la informacin correspondiente a

this.x = x; i 12 Tratamiento de errores mediante excepciones 819 la nueva instruccin throw: //: except ions/RethrowNew.java // Regeneracin de un objeto distinto de aqul que fue capturado. class OneException extends Exception { public OneException(String s) { super(s); )

) class TwoException *xt#ririfi Except-inn ( public TwoException(Strina s) { super(s); |

) public class RethrowNew ( public static void ft) throws OneException { System.out .println l "originating the exception in f () ") ; throw new OneException("thrown from f()M); I public static void main(String[] args) { try ( try { f0J } catch(OneException e) { System.out.println( "Caught in inner try, e.printStackTrace(J"); e.printStackTrace(System.out); throw new TwoException("from inner try"); i ) catch(TwoException e) ( System.out.println( "Caught in outer try, e.printStackTrace)"); e.printStackTrace(Sy9tera.out);

this.x = x; i 12 Tratamiento de errores mediante excepciones 820 )

) ) / Output: originating the exception in () Caught in inner try, e.printStackTrace0 OneException: thrown from {) at RethrowNew. f IRethrowNew. java: 15) at RethrowNew.main(RethrowNew.java:2 0) Caught in outer try. e.printStackTracet) TwoException: from inner try at RethrowNew.main(RethrowNew.java:25)

*///:-

La excepcin