Eclipse

Es una comunidad open source consistente en más de 150 proyectos que cubren diferentes aspectos del desarrollo de software. Entre otros aspectos, proporciona una entorno de desarrollo llamado Eclipse IDE

Versiones de Eclipse

Eclipse libera nuevas veriones periódicamente. En el siguiente gráfico puede verse como el peso de Eclipse ha ido cambiando con las diferentes versiones (siendo la última al escribir este artículo la versión 4.5, Eclipse Mars).

Eclipse es el entorno de desarrollo más utilizado para el desarrollo en Java, aunque también soporta lenguajes como C, C++, Python, Perl y PHP. Una vez hemos instalado Eclipse IDE, se puede extender su funcionalidad mediante el uso de plugins.

Eclipse IDE

Vamos a instalar la versión Eclipse IDE for Java Developers, que podemos descargar desde https://www.eclipse.org/downloads/. Eclipse IDE for Java Developers está pensado para el desarrollo en Java.

Prerequisitos

Para poder ejecutar Eclipse IDE for Java Developers, necesitamos tener instalado JDK, que podemos descargar desde http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Java Development Kit o (JDK), es un software que provee herramientas de desarrollo para la creación de programas en Java. Incluye entre otras cosas el compilador de Java javac y el intérprete de bytecode java

Instalación de Eclipse IDE for Java Developers

Una vez que hemos descargado el archivo de instalación de Eclipse, descomprimimos en un directorio local. Después ejecutamos el programa eclipse dentro de dicha carpeta.

Si obtuviste el mensaje "exit code=13"

Si obtuviste este mensaje al ejecutar Eclipse, quiere decir que hay una discordancia 32/64 bits entre Eclipse y JDK. Es decir:

  • O bien JDK es de 32 bits y Eclipse de 64
  • O bien JDK es de 64 bits y Eclipse de 32

La solución está en usar la opción correcta según el sistema operativo que estemos usando.

Empezando con Eclipsde

Para empezar a usar Eclipse, hacemos doble clic sobre el archivo eclipse.exe en el directorio donde descomprimimos Eclipse. Eclipse nos preguntará por nuestro espacio de trabajo:

El espacio de trabajo es la ubicación en el sistema de archivos donde Eclipse almacena sus preferencias y otros recursos. Por ejemplo, nuestros proyectos se almacenan en el espacio de trabajo. Si hemos elegido de manera definitiva la ubicación del espacio de trabajo, debemos seleccionar la casilla con el texto "Use this as the default and do not ask again".

Después, Eclipse arranca y muestra una página de bienvenida (Welcome). Podemos cerrar dicha página, entonces veremos la aplicación tal y como se muestra a continuación:

Poner eclipse en español

Babel es uno de los proyectos de Eclipse. Proporciona paquetes de idioma para el entorno. Para cambiar el idioma, primero debemos abrir la página el proyecto Babel. Allí copiamos el enlace marcado a continuación.

Una vez copiado el enlace, debemos instalar el nuevo software, desde Help\Install New Software. En la ventana que se abrirá, debemos pegar el enlace en la entrada de texto "Work with":

Debemos bajar por la lista de paquetes hasta encontrar el paquete de español. Después, lo seleccionamos y pulsamos el botón "Next". Una vez que se instale el paquete, veremos lo siguiente:

Finalmente seleccionamos el botón de radio "I accept the terms of the license agreement", y hacemos clic en "Finish".

En el próximo reinicio, Eclipse estará en Español.

Si además queremos modificar el aspecto del IDE, podemos ir a Ventana\Preferencias\General\Aspecto.

Terminología de Eclipse

Espacio de trabajo

El espacio de trabajo es la ubicación física en la que estamos trabajando. Esto implica nuestros proyectos, archivos de código fuente, imágenes y otros elementos. También se almacenan en el espacio de trabajo las configuraciones, metadatos de los plugins, logs, etc.

Podemos desear contar con más de un espacio de trabajo, para tener diferentes configuraciones. Para crear cambiar de espacio de trabajo, podemos ir a Archivo\Cambiar espacio de trabajo. Si aun no se han creado nuevos espacios de trabajo, solamente veremos la opción "Otras". Desde allí podemos crear un nuevo espacio de trabajo, indicando la nueva ubicación:

En esta misma ventana podemos ver las siguientes opciones, bajo el menú "Copiar configuración":

  • Disposición del entorno de trabajo: Vistas abiertas, tamaño y perspecitivas
  • Conjuntos de trabajo: Agrupaciones de proyectos creados manualmente para organizarlos

Para cambiar de un espacio de trabajo a otro, es preciso que tengan ubicaciones diferentes.

Proyectos

Un proyecto contiene código fuente, configuración y archivos binarios relacionados con una cierta tarea. Cada proyecto puede ser de cierta naturaleza, que describe el propósito del proyecto. La naturaleza del proyecto viene definida en el archivo .project.

Un proyecto no puede contener otro proyecto.

Partes

Las partes son componentes de la interfaz de usuario que permiten navegar y modificar datos. Una parte puede tener un menú desplegable, menús de contexto y una barra de herramientas. Las partes pueden ser apiladas o colocadas junto a otras dependiendo del contenedor en que estén:

Las partes pueden ser Vistas o Editores. Por ejemplo, el "Explorador de paquetes" es una vista. La ventana donde escribimos el código fuente es un editor.

Perspectiva

Una perspectiva es un contenedor opcional para conjuntos o pilas de partes. Por ejemplo, Eclipse los utiliza para mostrar las vistas necesarias para una cierta tarea (desarrollo, depuración, revisión,...).

Los editores abiertos en una perspectiva, se mantienen abiertos al cambiar de perspectiva. Para cambiar de una a otra, podemos ir a Ventana\Perspective. Las perspectivas habituales cuando estamos desarrollando en Java, son Java y Depurar.

Podemos cambiar la composición de una perspectiva abriendo o cerrando partes. Para abrir una nueva parte en la perspectiva actual, usamos la opción Ventana\Mostrar vista.

Si queremos devolver una perspectiva a su estado original, podemos usar la opción Ventana\Perspective\Restablecer perspectiva.

También podemos guardar la perspectiva actual con un nombre propio, en la opción Ventana\Guardar perspectiva como....

Podemos usar la opción Ventana\Perspective\Personalizar perspective para cambiar el comportamiento, no solo de las partes, sino de la barra de herramientas.

La perspectiva Java y el Explorador de Paquetes

La perspectiva por defecto para el desarrollo en Java es "Java", y se puede abrir en la opción Ventana\Perspective\Abrir perspectiva\Otras\Java. En esta perspectiva podemos ver:

  • Barra de herramientas (en la parte superior): incluye acciones típicas, tales como "Crear un nuevo proyecto".
  • Explorador de paquetes (en la parte de la izquierda): permite navegar por los diferentes proyectos y seleccionar con doble clic los elementos que queremos abrir en un editor.
  • Editores de Java abiertos (en la parte central): Uno o más editores que pueden abrirse a través de las pestañas de archivo.
  • Esquema (en la parte derecha): el esquema muestra la estructura del archivo de código fuente seleccionado actualmente.
  • Task list (en la parte derecha): permite crear listas de tareas asociadas a un cierto archivo.
  • Problmeas (en la parte inferior): muestra los errores y mensajes de advertencia relacionados con los proyectos. Los mensajes mostrados se pueden configurar en el menú desplegable de la vista. Por ejemplo, para mostrar los problemas sólamente del proyecto actual, seleccionamos configurar contenidos, para seleccionar el ámbito "En cualquier elemento del mismo proyecto".
  • Javadoc (en la parte inferior): Muestra la documentación del elemento seleccionado.

Crear un nuevo proyecto

Vamos a crear un pequeño proyecto java. En primer lugar hay que crear la carpeta donde se guardará el proyecto. Para ello, hacemos seleccionamos el menú Archivo\Nuevo\Proyecto Java. Entonces, aparecerá una diálogo en el que debemos indicar algunas cuestiones sobre el proyecto.

  • Nombre del proyecto: podemos usar alguna convención de nombres para organizar nuestros proyectos. Una de las formas más usadas, consiste en utilizar nombres de dominio completamente cualificados (FQDN), escritos a la inversa. Por ejemplo net.mauriciomatamala.eclipse.primer_proyecto
  • Utilizar ubicación predeterminada: se utilizará el espacio de trabajo por defecto (que se puede cambiar en Archivo\Cambiar espacio de trabajo
  • JRE: esta sección indica que versión de java estamos usando para la compilación y ejecución del proyecto. Por defecto se indica la versión de Java que tengamos instalado.
  • Java ha sufrido cambios a lo largo de su historia. Además, en cada momento han coexistido distintas versiones o distribuciones de Java con distintos fines. Actualmente puede considerarse que el Java vigente se denomina Java 2 y existen 3 distribuciones principales de Java 2, con ciertos aspectos comunes y ciertos aspectos divergentes.

    • J2SE o simplemente Java SE: Java 2 Standard Edition o Java Standard Edition. Orientado al desarrollo de aplicaciones cliente / servidor. No incluye soporte a tecnologías para internet. Es la base para las otras distribuciones Java y es la plataforma que utilizaremos nosotros en este curso por ser la más utilizada.
    • J2EE: Java 2 Enterprise Edition. Orientado a empresas y a la integración entre sistemas. Incluye soporte a tecnologías para internet. Su base es J2SE.
    • J2ME: Java 2 Micro Edition. Orientado a pequeños dispositivos móviles (teléfonos, tabletas, etc.).
  • Diseño de proyecto: permite elegir si creamos una única carpeta para código fuente y archivos bytecode, o bien si creamos carpetas separadas.
  • Conjuntos de trabajo: permite crear agrupaciones de proyectos creados manualmente para organizarlos

Después hacemos clic en Finalizar. Podremos entonces ver el nuevo proyecto en el explorador de paquetes.

Crear un paquete

Un Paquete en Java es un contenedor de clases que permite agrupar las distintas partes de un programa cuya funcionalidad tienen elementos comunes.

Una buena política de nombres para el nombre del paquete, es usar el mismo nombre para el paquete principal y el proyecto. Para crear el paquete, seguimos los siguientes pasos:

  1. En el "Explorador de paquetes", desplegar el proyecto, y seleccionar la carpeta src.
  2. Hacer clic derecho sobre la carpeta src, y seleccionar Nuevo\Paquete.
  3. En la opción "Carpeta fuente" debe aparecer el nombre completo del proyecto, seguido por la carpeta src. Por ejemplo, net.mauriciomatamala.eclipse.primer_proyecto/src.
  4. En la opción "Nombre", debe aparecer el nombre del paquete, que en este caso coincide con el nombre del proyecto.
  5. En la casilla "Create package-info.java" se indica si se debe crear el archivo package-info.java en el paquete. Este archivo contiene anotaciones y documentación relativa al paquete. Dejamos la opción sin seleccionar.
  6. El archivo package-info.java puede añadirse a cualquier paquete. Su objetivo es proporcionar un lugar donde guardar la documentación relativa al paquete. Este archivo contiene una declaración del paquete, así como documentación que después será extraida por el gestor de documentación que usemos (JavaDoc por ejemplo). Por ejemplo, el siguiente podría ser el contenido de este archivo en un paquete llamado

    /** * Clases usadas para crear salidas JSON y XML para servicios RESTful. * * Estas clases contienen anotaciones JAXB. * * @since 1.0 * @author jwhite * @version 1.1 */ package com.intertech.cms.domain;
  7. Finalmente, hacemos clic en "Finalizar".
  8. Una vez hecho esto nuestro nuevo paquete aparecerá en la carpeta src del proyecto.

    Crear una clase

    En el paquete creado anteriormente, hacemos clic derecho y elegimos la opción Nueva\Clase. Entonces se abrirá un diálogo, donde debemos indicar algunos detalles sobre la clase:

    • Carpeta fuente: indica la carpeta donde se ubicará la clase
    • Paquete: indica el paquete al que pertenece la clase
    • Tipo delimitador: si deseamos que nuestra clase esté dentro de otra, debemos especificarla aquí. Tenemos un asistente que nos ayudará a seleccionar la clase.
    • Nombre: indicamos aquí el nombre de la clase. Podemos poner cualquier nombre que deseemos. Por ejemplo MiPrimeraClase.
    • Modificadores: indica los modificadores de la clase.
    • Superclase: indica el nombre de la clase de la que hereda.
    • ¿Qué apéndices de método desea crear?: Es importante que en la primera clase que creamos, seleccionemos la opción public static void main(String[] args).

    Hacemos clic en "Finalizar", y la clase se crea.

    Para que nuestra clase haga algo, vamos a añadir una setencia que escribe un mensaje de tipo "Hola mundo". Para ello, debemos añadir la siguiente línea dentro de la función main:

    package net.mauriciomatamala.eclipse.primer_proyecto; public class MiPrimeraClase { public static void main(String[] args) { System.out.println("Hola mundo."); } }

    Ejecutar el proyecto

    Para ejecutar el proyecto, hacemos clic derecho sobre la clase Java que hemos creado en el explorador de paquetes, y elegimos la opción Ejecutar como\Aplicación Java. Entonces podremos ver el resultado de la ejecución en la venta Consola.

    Ejecutar un proyecto desde fuera de Eclipse

    Para ejecutar el programa Java fuera de Eclipse, es necesario exportarlo como un archivo JAR. Un archivo JAR es el formato estándar en el que se distribuyen aplicaciones Java.

    "Un archivo JAR (por sus siglas en inglés, Java ARchive) es un tipo de archivo que permite ejecutar aplicaciones escritas en el lenguaje Java. Las siglas están deliberadamente escogidas para que coincidan con la palabra inglesa "jar" (tarro). Los archivos JAR están comprimidos con el formato ZIP y cambiada su extensión a .jar." (Wikipedia)

    Los pasos a seguir son los siguientes:

    1. Elegimos nuestro proyecto, hacemos clic derecho sobre él y elegimos la opción "Exportar".
    2. Entonces, en la ventana que aparece, debemos seleccionar archivo JAR y hacer clic en el botón "Siguiente".
    3. Después elejimos nuestro proyecto, y elegimos el destino donde se exportará el programa así como el nombre del archivo JAR.
    4. Hacemos clic en el botón "Finalizar"

    En las capturas anteriores se puede apreciar que se marcan como recursos a exportar, los archivos ".classpath" y ".project". Cada cosa que podemos ver en Eclipse es el resultado de la ejecución de los plugins instalados, más que de Eclipse en sí.

    El archivo .project contiene una descripción del proyecto independiente a Eclipse. Incluye información sobre el nombre del proyecto, a qué otros proyectos hace referencia, compilador empleado, etc.

    El archivo .classpath es empleado por Eclipse para almacenar información sobre las carpetas que contienen el código fuente, las carpetas de salida, rutas a otros proyectos y archivos jar.

    En la exportación del archivo JAR, hay varias opciones:

    • Exportar los archivos .class generados y los recursos: Se incluyen los archivos .class (bytecode) así como los recursos utilizados en el proyecto (otros tipos de archivo como imágenes, vídeos, archivos xml, etc.).
    • Exportar todas las carpetas de salida de los proyectos marcados: Se incluyen todas las carpetas de salida en el archivo JAR (pero no el códig fuente)
    • Exportar los archivos fuente generados y los recursos: Se incluye el código fuente del programa así como los recursos adicionales. Usar esta opción expone el código fuente del programa.
    • Exportar refactorizaciones para proyectos seleccionados: Los scripts de refactorización para los proyectos seleccionados se incluyen en el JAR.

    Para poder ejecutar nuestra clase, exportada en el archivo JAR, debemos hacer lo siguiente:

    1. Abrimos una línea de comandos (CMD) y los dirigimos hasta la carpeta donde se exportó el archivo JAR. En mi caso, ejecutando el comando cd C:\Temp.
    2. Para ejecutar el programa, debemos incluir el archivo JAR en el classpath. Para ello, utilizamos el comando siguiente (en mi caso):
    java -classpath primer_programa.jar net.mauriciomatamala.eclipse.primer_proyecto.MiPrimeraClase

    Classpath en Java es el directorio o lista de directorios donde JRE busca archivos .class (bytecode). Se puede especificar mediante la variable de entorno CLASSPATH, empleando la opción -classpath en línea de comandos., o bien utilizando el el atributo Class-Path en el archivo de manifiesto manifest.mf dentro del archivo JAR.

    En Windows, podemos definir el classpath de forma permanente, creando una variable llamada CLASSPATH con ruta al directorio lib donde está instalado Java. Por ejemplo en mi caso, dicho directorio es C:\Program Files\Java\jdk1.8.0_51\lib\primer_programa.jar. Si hay varios archivos .jar a ejecutar, los añadiremos a esta variable separándolos mediante ";". Por ejemplo, CLASSPATH=C:\Program Files\Java\jdk1.8.0_51\lib\primer_programa.jar;C:\Program Files\Java\jdk1.8.0_51\lib\segundo_programa.jar

    Actividad 1. Crea un proyecto llamado net.tu_nombre.ed.agenda. Después crea en el proyecto un paquete con el nombre net.tu_nombre.ed.agenda.modelo. Finalmente, crea dos clases en el paquete llamadas Actividad y Jornada.

    Toma una captura donde se pueda observar en el explorador de paquetes, el proyecto, el paquete así como las clases creadas. Debe poder verse en un editor una de las dos clases. Guarda la captura con el nombre Act1-eclipse.png.

    Exportación e importación de proyectos

    Si necesitamos compartir proyectos con otras personas, eclipse permite exportar e importar proyectos.

    Para exportar un proyecto, elegimos la opción Archivo\Exportar. En la ventana que aparece, llamada "Exportar", elegimos la opción General\Archivo de archivado. En la ventana siguiente, dejamos seleccionadas las siguientes opciones:

    • El proyecto o proyectos a exportar (en la imagen solamente se puede ver un proyecto seleccionado, porque hay un único proyecto creado. Pero si hubiesen más de un proyecto creado en Eclipse, aparecerían todos).
    • Las opciones .classpath y .project.
    • La entrada de texto "Al archivo de archivado:" debe contener la ruta al archivo que contendrá el proyecto exportado.
    • La opción "Guardar con formato zip" y "comprimer el contenido del archivo".
    • La opción "Crear la estructura de directorios para archivos.

    Importar un proyecto

    Tenemos más de un mecanismo para importar un proyecto:

    • Importar a partir de un "archivo de archivado" (archivo comprimido)
    • Copiar la carpeta raíz del proyecto en nuestro espacio de trabajo (workspace) e importarlo
    • Importar directamente desde el otro espacio de trabajo

    Impoartar a partir de un "archivo archivador"

    Para importar un proyecto a partir de un .zip:

    1. Elegimos la opción Archivo\Importar.
    2. En la ventana que se abre, elegimos la opción General\Proyectos existentes en el espacio de trabajo.
    3. En la ventana siguiente seleccionamos el "archivo archivador" y hacemos clic en "Finalizar".

    Importar a partir del directorio raíz del proyecto

    El mecanismo es muy similiar:

    1. Elegimos la opción Archivo\Importar.
    2. En la ventana que se abre, elegimos la opción General\Proyectos existentes en el espacio de trabajo.
    3. En la ventana siguiente seleccionamos el "directorio raíz", y aparecerán los proyectos disponibles (ya sea en nuestro espacio de trabajo o en otro). Marcamos el proyecto a importar.
    4. Si estamos copiando desde un directorio diferente a nuestro espacio de trabajo, seleccionamos la casilla "copiar proyectos en el espacio de trabajo".
    5. Para terminar, hacemos clic en "Finalizar".

    Aparece una opción "Hide existing projects", que nos permite mover proyectos de un workspace a otro. De modo que si he copiado la carpeta de un proyecto y la he pegado en otro workspace, esta opción me mostrará solamente los proyectos que aun no se han añadido a los metadatos del Workspace.

    La lista de proyectos existentes en un workspace se encuentra en Ruta_al_workspace\.metadata\.plugins\org.eclipse.core.resources\.projects

    Actividad 2. Crea un nuevo proyecto (además del creado en la actividad 1), llamado net.tu_nombre.ed.planning. Después crea un nuevo espacio de trabajo llamado workspace2. Debes importar los dos proyectos (net.tu_nombre.ed.agenda y net.tu_nombre.ed.planning) empleando un procedimiento diferente para cada uno:

    • El proyecto net.tu_nombre.ed.agenda debes importarlo a partir de un "archivo archivador".
    • El segundo proyecto net.tu_nombre.ed.planning debes importarlo desde el primer espacio de trabajo.

    Este ejercicio contiene varias capturas:

    • Captura Act2.1-eclipse.png donde se puede apreciar el momento en que exportas el primer proyecto.
    • Captura Act2.2-eclipse.png donde se puede apreciar el momento en que creas el segundo espacio de trabajo.
    • Captura Act2.3-eclipse.png donde se puede apreciar el momento en que importas el primer proyecto.
    • Captura Act2.4-eclipse.png donde se puede apreciar el momento en que importas el segundo proyecto.
    • Captura Act2.5-eclipse.png donde se puede ver el explorador de paquetes con los proyectos importados. Para que quede claro que estás en el segundo espacio de trabajo, muestra en esta captura también las propiedades de uno de los proyectos, ya que se puede ver la ruta hasta el proyecto.

    Navegar por el código fuente

    El explorador de paquetes

    El explorador de paquetes es el recurso principal para navegar por el proyecto.

    También podemos filtrar los recursos a mostrar u ocultar.

    Los proyectos se pueden cerrar y abrir desde el explorador de paquetes. Para cerrar un paquete, hacemos clic con el botón derecho y elegimos la opción "Cerrar proyecto". Para abrirlo, hacemos clic sobre "Abrir proyecto". Las ventajas que tiene cerrar un proyecto son:

    • Eclipse ahorra memoria
    • Se ignoran los errores de los proyectos cerrados. De este modo pocemos centrarnos en los proyectos de interés.

    Si deseamos ocultar los proyectos cerrados, podemos usar el filtro del explorador de paquetes.

    Otra cosa que podemos hacer, es enlazar el archivo abierto en el editor de Java con el archivo en el explorador de paquetes. Supongamos que tenemos abiertos los archivos "MiPrimeraClase.java" y "MiSegundaClase.java", tendremos activo solo uno de los dos editores. Si activamos el botón enlazar con el editor, al cambiar el editor activo, en el explorador de paquetes quedará seleccionado el archivo correspondiente.

    Buscar una clase

    Pulsando Ctrl+Shfit+T podemos bucar una clase cualquiera. Se abrirá un diálogo llamado "Abrir tipo", donde podemos indicar las primeras letras de la clase que buscamos.

    También podemos buscar clases por nombre de paquete. Los nombres de clase por paquetes incluyen el carácter ".", como por ejemplo net.mauriciomatamala.eclipse.primer_proyecto.MiPrimeraClase. Para buscar esta clase por paquete, podemos indicar un parte de cada segmento del nombre del paquete. Por ejemplo n.m.e.pri.MiPr sería suficiente para localizar la clase net.mauriciomatamala.eclipse.primer_proyecto.MiPrimeraClase.

    El diálogo "Buscar tipo" también soporte el estilo "CamelCase" , de forma que se puede utilizar la primera letra de cada segmento del nombre. Por ejemplo, si pensamos en el nombre "MiPrimerClase", podemos hacer una búsqueda indicando "MPC"

    Una vez que hemos abierto un archivo ".java" con un editor, si deseamos encontrar un elemento dentro del archivo, podemos pulsar simultáneamente Ctrl+F3. Se nos mostrará un esquema flotante del archivo. Así podremos hacer clic sobre el elemento que buscamos. A esta opción (del esquema flontante) también podemos llegar haciendo clic derecho sobre el editor y eligiendo la opción "esquema flotante" o bien la combinación de teclas Ctrl+O.

    La "jerarquía de tipos" permiter la jerarquía de clases completa hasta la clase de más alto nivel. Para acceder a la jerarquía de tipos se puede hacer pulsando F4 o bien con la combinación de teclas Ctrl+T, o bien haciendo clic derecho sobre el editor y eligiendo la opción "Abrir jerarquía de tipos" o bien "Abrir jerarquía de tipos flotante".

    Búsquedas generales

    Para hacer búsquedas más generales, podemos usar la opción Buscar\Buscar en la pestaña "Búsqueda Java". Por ejemplo, para buscar el paquete net.mauriciomatamala.eclipse.primer_proyecto, podemos marcar el botón de selección paquete, y buscar "net.maur*".

    Cuando se hacen búsquedas que implican más de una coincidencia, podemos ver en la ventana "Buscar" las coincidencias. Además, en el margen izquierdo de cada editor aparecerá una flecha amarilla indicando la ubicación de la coincidencia. En la imagen siguiente se puede apreciar el resultado de la búsqueda del método "println".

    También podemos buscar textos dentro de archivos, utilizando la pestaña "Búsqueda de archivos".

    Es posible elegir qué pestañas de búsqueda aparecen en la ventana de búsqueda, pulsando el botón "personalizar"

    Búsqueda incremental

    La búsqueda incremental permite buscar dentro de un archivo sin necesidad de abrir una ventana de búsqueda. Para usar la búsqueda incremental sobre un fichero basta con abrirlo en el editor y pulsar Ctrl+J. No aparecerá ningún diálogo ni nada visual salvo la frase "Búsqueda incremental" en la barra de estado. En ese momento tecleamos la palabra a buscar y Eclipse irá buscando según tecleamos. Para moverte entre las distintas ocurrencias se pueden usar las fechas arriba y abajo, o bien volver a pulsar Ctrl+J.

    Búsqueda de selección

    Si tenemos un elemento seleccionado en el editor, podemos usar la combinación Ctrl+K para buscar la siguiente ocurrencia del texto seleccionado, y Ctrl+Shift+K para el elemento previo

    Búsqueda de anotaciones

    Supongamos que tenemos un código con errores. Para ello, vamos a fijarnos en la clase MiSegundaClase que contiene el siguiente método:

    public otro(){ System.out.printl("Otro texto"); }

    Eclipse mostrará en la columna izquierda indicadores de error. Estos indicadores pueden recorrerse empleando los botones Siguiente Anotación y Anotación anterior, tal y como se muestra en la imagen

    Entre los botones de siguiente y anterior anotación, hay un menú desplegable que permite elegir qué anotaciones se destacarán.

    Hilo de Ariadna

    Para mostrar la navegación por "Hilo de Ariadna" podemos hacer clic derecho en el editor y elegir la opción "Mostrar el hilo de Ariadna".

    Para volver a ocultarlo, podemos hacer clic derecho sobre una entrada del hilo y elegir la opción "Ocultar"

    Abrir un recurso

    Otra alternativa que ofrece Eclipse para abrir un recurso, es la ventana abrir recurso. Desde esta ventana podemos acceder a cualquier recurso de cualquier proyecto abierto. Para abrirla, usaremos la combinación de teclas Ctrl+Shift+R. Con solo escribir un indicio del nombre del recurso, se mostrarán todos los recursos coincidentes.

    Asistente de contenido

    Esta funcionalidad permite el autocompletado del código fuente. Por ejemplo. Si escribimos System.out. y a continuación pulsamos la combinación de teclas Ctrl+Espacio, se nos mostrarán las opciones de autocompletado.

    En este caso podemos ver los métodos que contiene la clase out.

    Sugerencias de corrección

    Eclipse ofrece opciones de corrección cuando detecta un error en el código. En la imagen siguiente se puede ver una asignación sobre miVariable sin haber definido previamente qué es miVariable.

    Si colocamos el ratón sobre el subrayado y pulsamos la opción Ctrl+1, obtenemos una ventana con varias sugerencias. Dependiendo de la opción elegida, el código se modificará en consecuencia.

    Generación de código

    Cuando se crea una nueva clase, existen ciertas recomendaciones estándar para garantizar la calidad del código. Entre estas recomendaciones están:

    • Los atributos de la clase no deben ser públicos
    • El acceso a los atributos de la clase debe hacerse mediante funciones get y set
    • Se deben crear constructores que permitan asignar valor a los atributos al crear una instancia de la clase.

    Eclipse permite crear código fuente en este sentido. Se puede encontrar en la opción Código fuente.

    Supongamos una clase llamada Ubicacion con el siguiente código fuente

    Si queremos crear las funciones Set y Get, debemos seleccionar los atributos de la clase, e ir a la opción Código fuente\Generar métodos de obtención y establecimiento.... Entonces podremos ver la siguiente ventana, donde elegir las opciones a usar durante la generación de código:

    Una vez creados los métodos Set y Get, nuestro código quedará como sigue:

    Hay varias opciones en el menú:

    • Alterar temporalmente/Implementar métodos: Permite sobreescribir los métodos de la superclase.
    • Generar métodos de obtención y establecimiento: Permite crear las funciones Set y Get.
    • Generar métodos delegados: Permite generar métodos para un atributo delegado (ver ejemplo más abajo).
    • Generar toString(): Genera una función que devuelve una cadena de texto con el contenido de la clase.
    • Generar hashCode() y equals(): Crea las funciones hashCode y equals. Estas funciones deben redefinirse para cada clase que se vaya a utilizar en colecciones de objetos, como Set o ArrayList (una buena explicación).
    • Generar constructor utilizando campos: Permite crear constructores que instancian los atributos de la clase.
    • Generar constructores de la superclase: Crear un constructor que invoca el constructor de la superclase.

    Para explicar como generar métodos delegados, vamos a pensar en la siguiente clase:

    public class MiString{ private String stringDelegada; }

    Si lo que queremos es crear métodos para trabajar directamente con la clase MiString en lugar de tener que extraer el atributo stringDelegada para trabajar sobre él, podemos usar la opción Codigo fuente\Generar métodos delegados.

    Actividad 3. Crea un nuevo paquete llamado net.tu_nombre.ed.planning.modelo en el proyecto net.tu_nombre.ed.planning, y a su vez, dentro crea una clase llamada HistoriaUsuario. Dicha clase contendrá los siguientes atributos privados:

    • String id
    • String descripcion
    • int valor
    • int coste

    Después genera el siguiente código en la clase:

    • Constructor de clase utilizando sólo los atributos id y descripcion.
    • Constructor de clase utilizando todos los atributos.
    • Métodos Set y Get para cada tributo.
    • Métodos equals y hashCode.
    • Método toString.

    Exporta el proyecto (incluyendo el código fuente) en un archivo llamado Act4-eclipse.zip.

    Refactorización

    La refactorización es un proceso mediante el que se reestructura el código sin cambiar su comportamiento, con la intención de limpiar el código. Limpiamos el código para mejorar la consistencia interna del código y la claridad. Esta limpieza incluye algunas reglas básicas*:

    • No duplicaciones: Evitar dos líneas en el código fuente que hagan lo mismo.
    • Expresividad: Utilizar nombres y nomenclaturas que pueda entender alguien que nunca antes había visto el código.
    • Clases y métodos pequeños: Es mejor una clase pequeña que una grande. Es mejor un método pequeño que uno grande. Cuando tenemos clases o métodos grandes, hay que plantearse si es posible dividirlos en unidades más pequeñas.

    Estas reglas básicas se corresponden a las reglas 2, 3 y 4 de lo que Kent Beck llama "Diseño Simple".

    Redenominar

    Qué hace: Renombra el elemento seleccionado y corrige todas las referencias al mismo.

    Dónde está disponible: Métodos, parámetros de métodos, atributos, variables locales, clases, constantes, paquetes, carpetas de código fuente, proyectos, etc.

    Un ejemplo: En el siguiente código aparece el atributo nombre, pero deseamos cambiarlo a descripcion. Del mismo modo, queremos cambiar los nombres de los métodos Set y Get de dicho atributo, no solo en la clase Ubicacion, sino en cualquier llamada a los métodos desde otra clase.

    // Archivo: Ubicacion.java package net.mauriciomatamala.eclipse.sitio.modelo; public class Ubicacion { private long latitud; private long longitud; private String nombre; public Ubicacion() { latitud = 0; longitud = 0; nombre = ""; } public long getLatitud() { return latitud; } public void setLatitud(long latitud) { this.latitud = latitud; } public long getLongitud() { return longitud; } public void setLongitud(long longitud) { this.longitud = longitud; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } } ----------------------------------------------------- // Archivo: Direccion.java package net.mauriciomatamala.eclipse.sitio.modelo; public class Direccion { Ubicacion ubicacion; String calle, piso; int numero, CP; public Direccion() { super(); this.ubicacion = new Ubicacion(); } public Ubicacion getUbicacion() { return ubicacion; } public void setUbicacion(Ubicacion ubicacion) { this.ubicacion.setLatitud(ubicacion.getLatitud()); this.ubicacion.setLongitud(ubicacion.getLongitud()); this.ubicacion.setNombre(ubicacion.getNombre()); } public String getCalle() { return calle; } public void setCalle(String calle) { this.calle = calle; } public String getPiso() { return piso; } public void setPiso(String piso) { this.piso = piso; } public int getNumero() { return numero; } public void setNumero(int numero) { this.numero = numero; } public int getCP() { return CP; } public void setCP(int cP) { CP = cP; } }

    Vamos a hacer los siguientes renombrados:

    • El atributo nombre de la clase Ubicacion pasa a llamarse descripcion.
    • El método setNombre de la clase Ubicacion pasa a llamarse setDescripcion.
    • El método getNombre de la clase Ubicacion pasa a llamarse getDescripcion.

    Observa como cambian los nombres tanto en el archivo Ubicacion.java como en el archivo Direccion.java.

    Mover

    Qué hace: Mueve una clase de un paquete a otro, y se cambian todas las referencias.

    Un ejemplo: Supongamos que estamos escribiendo una aplicación para almacenar información sobre sitios, donde se guardan direcciones, coordenadas e imágenes de los sitios. El código que disponemos es el siguiente (observa los nombres de paquete):

    // Archivo: Ubicacion.java package net.mauriciomatamala.eclipse.sitio.modelo; public class Ubicacion { private long latitud; private long longitud; private String descripcion; public Ubicacion() { latitud = 0; longitud = 0; descripcion = ""; } public long getLatitud() { return latitud; } public void setLatitud(long latitud) { this.latitud = latitud; } public long getLongitud() { return longitud; } public void setLongitud(long longitud) { this.longitud = longitud; } public String getDescripcion() { return descripcion; } public void setDescripcion(String nombre) { this.descripcion = nombre; } } ------------------------------------------------------- // Archivo: Direccion.java package net.mauriciomatamala.eclipse.sitio.modelo; public class Direccion { Ubicacion ubicacion; String calle, piso; int numero, CP; public Direccion() { super(); this.ubicacion = new Ubicacion(); } public Direccion(Ubicacion ubicacion, String calle, int numero, String piso, int CP) { super(); if (ubicacion != null){ ubicacion.setDescripcion(ubicacion.getDescripcion()); ubicacion.setLatitud(ubicacion.getLatitud()); ubicacion.setLongitud(ubicacion.getLongitud()); } this.calle = calle; this.piso = piso; this.numero = numero; this.CP = CP; } public Ubicacion getUbicacion() { return ubicacion; } public void setUbicacion(Ubicacion ubicacion) { this.ubicacion.setLatitud(ubicacion.getLatitud()); this.ubicacion.setLongitud(ubicacion.getLongitud()); this.ubicacion.setDescripcion(ubicacion.getDescripcion()); } public String getCalle() { return calle; } public void setCalle(String calle) { this.calle = calle; } public String getPiso() { return piso; } public void setPiso(String piso) { this.piso = piso; } public int getNumero() { return numero; } public void setNumero(int numero) { this.numero = numero; } public int getCP() { return CP; } public void setCP(int cP) { CP = cP; } } ----------------------------------------------------------------- // Archivo: Sitio.java package net.mauriciomatamala.eclipse.sitio.modelo; import java.util.ArrayList; public class Sitio { int id; Direccion direccion; ArrayList<String> imagenes; public Sitio(){ direccion = new Direccion(); imagenes = new ArrayList<String>(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public Direccion getDireccion() { return direccion; } public void setDireccion(Direccion direccion) { this.direccion.setCalle(direccion.getCalle()); this.direccion.setCP(direccion.getCP()); this.direccion.setNumero(direccion.getNumero()); this.direccion.setPiso(direccion.getPiso()); this.direccion.setUbicacion(direccion.getUbicacion()); } public ArrayList<String> getImagenes() { return imagenes; } public void setImagenes(ArrayList<String> imagenes) { this.imagenes = imagenes; } } ---------------------------------------------------- // Archivo: Sitios.java package net.mauriciomatamala.eclipse.sitio.controlador; import java.util.Iterator; import net.mauriciomatamala.eclipse.sitio.modelo.Sitio; public class Sitios { ArrayList<Sitio> sitios; public Sitios(){ sitios = new ArrayList<Sitio>(); } public void addSitio(Sitio sitio){ sitios.add(sitio); } public Sitio getSitio(int id_sitio){ Iterator<Sitio> iterador_sitios = sitios.iterator(); Sitio sitioInspeccionado = null; while (iterador_sitios.hasNext()){ sitioInspeccionado = iterador_sitios.next(); if (sitioInspeccionado.getId() == id_sitio) return sitioInspeccionado; } return sitioInspeccionado; } public int size(){ return sitios.size(); } public Sitio getSitioPos(int pos){ return sitios.get(pos); } }

    Vamos a hacer el siguiente movimiento: mover el archivo Sitio.java al paquete net.mauriciomatamala.eclipse.sitios.

    Observa cómo cambia el nombre de paquete en el archivo, y se hacen las importaciones necesarias.

    Cambiar signatura de método

    Qué hace: Cambia nombres de parámetro, tipos, orden entre los parámetros y actualiza las referencias al método. Además, pueden cambiar el tipo devuelto, las excepciones lanzadas, etc.

    Dónde está disponible: En métodos

    Un ejemplo: Vamos a añadir a la clase Sitio el siguiente método constructor:

    public Sitio(int id, Direccion direccion, ArrayList<String> imagenes) { super(); this.id = id; this.direccion = direccion; this.imagenes = imagenes; }

    También vamos a añadir el siguiente constructor a la clase Direccion

    public Direccion(Ubicacion ubicacion, String calle, String piso, int numero, int CP) { this.ubicacion = ubicacion; this.calle = calle; this.numero = numero; this.piso = piso; this.CP = CP; }

    Además, vamos a crear una nueva clase, llamada MisSitios con el siguiente código:

    package net.mauriciomatamala.eclipse.sitio.controlador; import java.util.ArrayList; import net.mauriciomatamala.eclipse.sitio.modelo.Direccion; import net.mauriciomatamala.eclipse.sitio.modelo.Sitio; import net.mauriciomatamala.eclipse.sitio.modelo.Ubicacion; public class MisSitios { private static Sitio crearSitio(int id, Ubicacion ubicacion, String calle, String piso, int numero, int CP, ArrayList<String> imagenes){ Direccion direccion = new Direccion(ubicacion, calle, piso, numero, CP); Sitio nuevoSitio = new Sitio(id,direccion,imagenes); return nuevoSitio; } public static void main(String[] args) { // TODO Apéndice de método generado automáticamente Sitios sitios = new Sitios(); Sitio sitio1 = crearSitio(1,null,"C/Rue","3ºC",13,29004,null); Sitio sitio2 = crearSitio(1,null,"C/Pla","2ºB",11,29008,null); Sitio sitio3 = crearSitio(1,null,"C/Ruth","4ºC",3,29015,null); sitios.addSitio(sitio1); sitios.addSitio(sitio2); sitios.addSitio(sitio3); } }

    Ahora, utilizando "Cambiar Signatura de método", vamos a cambiar la signatura del constructor de Direccion para que el número aparezca antes que el piso.

    Podremos comprobar que la llamada que se hace desde la clase MisSitios también ha cambiado el orden.

    Extraer método

    Qué hace: Crea un nuevo método que contiene sentencias o expresiones seleccionadas y reemplaza la selección con una referencia al nuevo método. Esta característica es adecuada para limpiar métodos que sean demasiado largos, desestructurados o complicados (según Robert Martin, un método largo es aquel que incluye más de 4 líneas).

    Dónde está disponible: En conjuntos de instrucciones válidas.

    Un ejemplo: Supongamos que el método main de la clase MisSitios incluye el siguiente (terrible) código fuente:

    public static void main(String[] args) { // TODO Apéndice de método generado automáticamente Sitios sitios = new Sitios(); Sitio sitio1 = crearSitio(1,null,"C/Rue","3ºC",13,29004,null); Sitio sitio2 = crearSitio(1,null,"C/Pla","2ºB",11,29008,null); Sitio sitio3 = crearSitio(1,null,"C/Ruth","4ºC",3,29015,null); sitios.addSitio(sitio1); sitios.addSitio(sitio2); sitios.addSitio(sitio3); System.out.println("Los sitios almacenados son los siguientes:"); for (int i = 0; i < sitios.size(); i++){ System.out.println("-----------------------"); Sitio sitio = sitios.getSitioPos(i); System.out.println("ID Sitio: " + sitio.getId()); Direccion direccion = sitio.getDireccion(); System.out.println("Calle: " + direccion.getCalle()); System.out.println("Número" + direccion.getNumero()); System.out.println("Piso:" + direccion.getPiso()); System.out.println("CP:" + direccion.getCP()); Ubicacion ubicacion = direccion.getUbicacion(); if (ubicacion != null){ System.out.println("Coordenadas: " + ubicacion.getLatitud() + "(Lat); " + ubicacion.getLongitud() + "(Long)"); System.out.println("Descripción: " + ubicacion.getDescripcion()); } } }

    Vamos a extraer el código que incluye la publicación de la información de los sitios a un método con signatura mostrarSitios(Sitios sitios).

    Extraer variable local

    Qué hace: Crea una variable que contiene el valor de una expresión seleccionada y reemplaza la selección por el nombre de la variable.

    Dónde está disponible: Selecciones de código que sean válidas en la asignación de una variable.

    Un ejemplo: Si nos fijamos en en el método creado en el ejemplo anterior (mostrarSitios) observaremos que contiene la siguiente línea:

    System.out.println("Los sitios almacenados son los siguientes:");

    Aunque no sea realmente útil lo que vamos a hacer, si que es ilustrativo. Podemos seleccionar la cadena de texto Los sitios almacenados son los siguientes: y extraerlo a una variable local llamada cartelInicioInforme

    Extraer constante

    Qué hace: Es similar al anterior, solo que creando una constante a partir de la expresión seleccionada.

    Dónde está disponible: Selecciones de código que sean válidas en la asignación de una variable.

    Un ejemplo: Podemos repetir el ejemplo anterior, pero en este caso extrayendo el texto seleccionado a una constante.

    Incorporar

    Qué hace: Sustituye una referencia a una variable con el valor asignado a la variable para unir dos líneas en una.

    Dónde está disponible: Métodos, atributos de clase static final, y variables locales.

    Un ejemplo: Si nos fijamos, en el método mostrarSitios de la clase MisSitios aparece el siguiente fragmento de código:

    Ubicacion ubicacion = direccion.getUbicacion(); if (ubicacion != null){ System.out.println("Coordenadas: " + ubicacion.getLatitud() + "(Lat); " + ubicacion.getLongitud() + "(Long)"); System.out.println("Descripción: " + ubicacion.getDescripcion()); }

    Podríamos aplicar esta factorización sobre la variable ubicación.

    Convertir variable local en campo

    Qué hace: Convierte una variable local en un atributo de clase.

    Dónde está disponible: Variables locales.

    Un ejemplo: En la clase MisSitios se utiliza una variable llamada sitios. Esta variable se declara en el método main y después de pasa como parámetro al método mostrarSitios. Vamos a hacer dos cosas:

    • Convertir la variable sitios en un atributo de clase (campo).
    • Redefinir la signatura del método mostrarSitios para que no se le pase ningún parámetro.

    Convertir clase anónima en anidada

    Qué hace: Convierte una clase anónima en una clase anidada de la clase contenedora.

    Dónde está disponible: clases anónimas

    Un ejemplo: Supongamos que tenemos las siguientes clases, Tiempo y ClaseInterna:

    import java.text.DecimalFormat; public class Tiempo{ private int hora; // 0 - 23 private int minuto; // 0 - 59 private int segundo; // 0 - 59 // un objeto de formato para compartir en toString y aStringUniversal private static DecimalFormat dosDigitos = new DecimalFormat( "00" ); // el constructor de Tiempo inicializa cada variable de instancia en cero; // se asegura que cada objeto Tiempo inicie en un estado consistente public Tiempo(){ this( 0, 0, 0 ); // invocar al constructor de Tiempo con tres argumentos } // constructor de Tiempo: se proporciona hora; minuto y segundo con valor predeterminado de 0 public Tiempo( int h ){ this( h, 0, 0 ); // invocar al constructor de Tiempo con tres argumentos } // constructor de Tiempo: se proporcionan hora y minuto, segundo con valor predeterminado de 0 public Tiempo( int h, int m ){ this( h, m, 0 ); // invocar al constructor de Tiempo con tres argumentos } // constructor de Tiempo: se proporcionan hora, minuto y segundo public Tiempo( int h, int m, int s ){ establecerTiempo( h, m, s ); } // constructor de Tiempo: se suministra otro objeto Tiempo3 public Tiempo( Tiempo tiempo ){ // invocar al constructor de Tiempo con tres argumentos this( tiempo.obtenerHora(), tiempo.obtenerMinuto(), tiempo.obtenerSegundo() ); } /******************* Métodos Establecer ó Set *******************/ // establecer un nuevo valor de tiempo, utilizando la hora universal; realizar // comprobaciones de validez en los datos; establecer valores inválidos en cero public void establecerTiempo( int h, int m, int s ){ establecerHora( h ); // establecer la hora establecerMinuto( m ); // establecer el minuto establecerSegundo( s ); // establecer el segundo } // validar y establecer hora public void establecerHora( int h ){ hora = ( ( h >= 0 && h < 24 ) ? h : 0 ); } // validar y establecer minuto public void establecerMinuto( int m ){ minuto = ( ( m >= 0 && m < 60 ) ? m : 0 ); } // validar y establecer segundo public void establecerSegundo( int s ){ segundo = ( ( s >= 0 && s < 60 ) ? s : 0 ); public void establecerMinuto(int m){ minuto = (( m >= 0 && m < 60) ? m : 0); } public void establecerSegundo(int s){ segundo = (( s >= 0 && s < 60) ? s : 0); } /********* Métodos Obtener ó Get *************/ // obtener valor de hora public int obtenerHora(){ return hora; } // obtener valor de minuto public int obtenerMinuto(){ return minuto; } // obtener valor de segundo public int obtenerSegundo(){ return segundo; } // convertir a String en formato de hora universal public String aStringUniversal(){ return dosDigitos.format( obtenerHora() ) + ":" + dosDigitos.format( obtenerMinuto() ) + ":" + dosDigitos.format( obtenerSegundo() ); } // convertir a String en formato de hora estándar public String toString(){ return ( (obtenerHora() == 12 || obtenerHora() == 0 ) ? 12 : obtenerHora() % 12 ) + ":" + dosDigitos.format( obtenerMinuto() ) + ":" + dosDigitos.format( obtenerSegundo() ) + ( obtenerHora() < 12 ? " AM" : " PM" ); } } // fin de la clase Tiempo
    import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ClaseInterna extends JFrame { private static final long serialVersionUID = -8824256700475003106L; private Tiempo tiempo; private JLabel horaEtiqueta, minutoEtiqueta, segundoEtiqueta; private JTextField horaCampo, minutoCampo, segundoCampo, pantallaCampo; private JPanel contenedor; // constructor public ClaseInterna() { // llamar al constructor de JFrame para establecer cadena de barra de título super( "Demostración de clase interna anónima" ); tiempo = new Tiempo(); // crear objeto Tiempo crearGUI(); // configurar GUI super.add(this.contenedor); super.setResizable(false); registrarManejadoresDeEventos(); // configurar el manejo de eventos } // crear componentes de GUI y adjuntarlos al panel de contenido private void crearGUI(){ contenedor = new JPanel(); contenedor.setLayout(new BorderLayout()); JPanel contCampos = new JPanel(); contCampos.setLayout(new GridLayout(3, 2)); horaEtiqueta = new JLabel( "Ajuste hora" ); horaCampo = new JTextField( 10 ); minutoEtiqueta = new JLabel( "Ajuste minuto" ); minutoCampo = new JTextField( 10 ); segundoEtiqueta = new JLabel( "Ajuste segundo" ); segundoCampo = new JTextField( 10 ); pantallaCampo = new JTextField( 30 ); pantallaCampo.setEditable( false ); contCampos.add( horaEtiqueta); contCampos.add( horaCampo); contCampos.add( minutoEtiqueta); contCampos.add( minutoCampo); contCampos.add( segundoEtiqueta); contCampos.add( segundoCampo); contenedor.add(contCampos, BorderLayout.WEST); contenedor.add(pantallaCampo, BorderLayout.CENTER); contenedor.setSize(300, 250); } // fin del método crearGUI // registrar manejadores de eventos para horaCampo, minutoCampo y segundoCampo private void registrarManejadoresDeEventos(){ // registrar manejador de eventos para horaCampo horaCampo.addActionListener( new ActionListener() { // clase interna anónima public void actionPerformed( ActionEvent evento ) { tiempo.establecerHora( Integer.parseInt(evento.getActionCommand() ) ); horaCampo.setText( "" ); mostrarTiempo(); } } // fin de la clase interna anónima ); // fin de la llamada a addActionListener para horaCampo // registrar manejador de eventos para minutoCampo minutoCampo.addActionListener( new ActionListener() { // CLASE INTERNA ANONIMA public void actionPerformed( ActionEvent evento ){ tiempo.establecerMinuto( Integer.parseInt(evento.getActionCommand() ) ); minutoCampo.setText( "" ); mostrarTiempo(); } } // fin de la clase interna anónima ); // fin de la llamada a addActionListener para minutoCampo segundoCampo.addActionListener( new ActionListener() { // clase interna anónima public void actionPerformed( ActionEvent evento ) { tiempo.establecerSegundo( Integer.parseInt(evento.getActionCommand() ) ); segundoCampo.setText( "" ); mostrarTiempo(); } } // fin de la clase interna anónima ); // fin de la llamada a addActionListener para segundoCampo } // fin del método registrarManejadoresDeEventos // mostrar tiempo en pantallaCampo public void mostrarTiempo(){ pantallaCampo.setText( "La hora es: " + tiempo ); } // crear objeto VentanaPruebaTiempo2, registrarse para sus eventos de ventana // y mostrarlo para empezar la ejecución de la aplicación public static void main( String args[] ){ ClaseInterna ventana = new ClaseInterna(); // registrar componente de escucha para evento windowClosing ventana.addWindowListener( // clase interna anónima para evento windowClosing new WindowAdapter() { // terminar la aplicación cuando el usuario cierra la ventana public void windowClosing( WindowEvent evento ){ System.exit( 0 ); } } // fin de la clase interna anónima ); // fin de la llamada a addWindowListener para ventana ventana.setSize( 400, 120 ); ventana.setVisible( true ); } // fin de main } // fin de la clase ClaseInterna

    Extraer superclase

    Qué hace: Extrae una superclase a partir de la clase seleccionada

    Dónde está disponible: Clases

    Un ejemplo: Supongamos que creamos un nuevo tipo de sitio, llamado SitioSinImagenes. Supongamos que su código es este

    package net.mauriciomatamala.eclipse.sitio; public class SitioSinImagenes { int id; Direccion direccion; String indicaciones; public SitioSinImagenes(){ direccion = new Direccion(); indicaciones = ""; } public SitioSinImagenes(int id, Direccion direccion, String indicaciones) { super(); this.id = id; this.direccion = direccion; this.indicaciones = indicaciones; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Direccion getDireccion() { return direccion; } public void setDireccion(Direccion direccion) { this.direccion.setCalle(direccion.getCalle()); this.direccion.setCP(direccion.getCP()); this.direccion.setNumero(direccion.getNumero()); this.direccion.setPiso(direccion.getPiso()); this.direccion.setUbicacion(direccion.getUbicacion()); } public String getIndicaciones() { return indicaciones; } public void setIndicaciones(String indicaciones) { this.indicaciones = indicaciones; } }

    Si nos fijamos, la clase Sitio es muy parecida a la recién creada SitioSinImagenes. Esto supone que estamos repitiendo código. Podemos renombrar la clase Sitio a SitioConImagenes, y extrar una superclase llamada Sitio a partir de SitioSinImagenes, que también sea superclase de SitioConImagenes. El código quedaría del siguiente modo:

    // ARCHIVO: Sitio.java // Esta es la superclase extraída package net.mauriciomatamala.eclipse.sitio; public class Sitio { protected int id; protected Direccion direccion; public Sitio() { super(); direccion = new Direccion(); } public Sitio(int id, Direccion direccion){ this.id = id; this.direccion = direccion; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Direccion getDireccion() { return direccion; } public void setDireccion(Direccion direccion) { this.direccion.setCalle(direccion.getCalle()); this.direccion.setCP(direccion.getCP()); this.direccion.setNumero(direccion.getNumero()); this.direccion.setPiso(direccion.getPiso()); this.direccion.setUbicacion(direccion.getUbicacion()); } } ------------------------------------------------------- // ARCHIVO: SitioConImagenes.java package net.mauriciomatamala.eclipse.sitio; import java.util.ArrayList; public class SitioConImagenes extends Sitio { int id; Direccion direccion; ArrayList<String> imagenes; public SitioConImagenes(){ super(); imagenes = new ArrayList<String>(); } public SitioConImagenes(int id, Direccion direccion, ArrayList<String> imagenes) { super(id,direccion); this.imagenes = imagenes; } public ArrayList<String> getImagenes() { return imagenes; } public void setImagenes(ArrayList<String> imagenes) { this.imagenes = imagenes; } } ------------------------------------------------------- // ARCHIVO: SitioSinImagenes.java package net.mauriciomatamala.eclipse.sitio; public class SitioSinImagenes extends Sitio { String indicaciones; public SitioSinImagenes(){ direccion = new Direccion(); indicaciones = ""; } public SitioSinImagenes(int id, Direccion direccion, String indicaciones) { super(id,direccion); this.indicaciones = indicaciones; } public String getIndicaciones() { return indicaciones; } public void setIndicaciones(String indicaciones) { this.indicaciones = indicaciones; } }

    Extraer interfaz

    Qué hace: Crea una nueva interfaz con un conjunto de métodos y hace que la clase seleccionada implemente la interfaz. Es similar a extraer una clase, pero en este caso, una interfaz.

    Dónde está disponible: Clases

    Por ejemplo, observa la siguiente clase:

    package net.mauriciomatamala.ed.eclipse.Cantes; public class Cantante { public void cantar(){ System.out.println("Tralará, tralarí"); } }

    De aquí podríamos extraer una interfaz llamada SabeCantar, que declara el método cantar. A partir de aquí, podemos crear una nueva clase llamada, por ejemplo, Canario, que implementa la interfaz SabeCantar.

    Degradar

    Qué hace: Mueve uno o varios métodos y/o atributos a una subclase.

    Dónde está disponible: Métodos y atributos de clase

    Promover

    Qué hace: Mueve uno o varios métodos y/o atributos a una superclase.

    Dónde está disponible: Métodos y atributos de clase

    Extraer clase

    Qué hace: Crea una clase a partir de un fragmento de una clase existente.

    Dónde está disponible: en atributos y métodos de una clase.

    Un ejemplo: Podríamos extraer los campos sueltos en dirección (calle, numero, etc.) por una clase llamada DireccionPostal que los contiene a todos. De este modo, la clase Direccion quedaría así.

    package net.mauriciomatamala.eclipse.sitio; public class Direccion { Ubicacion ubicacion; DireccionPostal direccionPostal; public Direccion() { super(); this.ubicacion = new Ubicacion(); this.direccionPostal = new DireccionPostal(); } public Direccion(Ubicacion ubicacion, String calle, int numero, String piso, int CP) { super(); if (ubicacion != null){ ubicacion.setDescripcion(ubicacion.getDescripcion()); ubicacion.setLatitud(ubicacion.getLatitud()); ubicacion.setLongitud(ubicacion.getLongitud()); } this.direccionPostal.setCalle(calle); this.direccionPostal.setPiso(piso); this.direccionPostal.setNumero(numero); this.direccionPostal.setCP(CP); } public Ubicacion getUbicacion() { return ubicacion; } public void setUbicacion(Ubicacion ubicacion) { this.ubicacion.setLatitud(ubicacion.getLatitud()); this.ubicacion.setLongitud(ubicacion.getLongitud()); this.ubicacion.setDescripcion(ubicacion.getDescripcion()); } public String getCalle() { return direccionPostal.getCalle(); } public void setCalle(String calle) { this.direccionPostal.setCalle(calle); } public String getPiso() { return direccionPostal.getPiso(); } public void setPiso(String piso) { this.direccionPostal.setPiso(piso); } public int getNumero() { return direccionPostal.getNumero(); } public void setNumero(int numero) { this.direccionPostal.setNumero(numero); } public int getCP() { return direccionPostal.getCP(); } public void setCP(int cP) { direccionPostal.setCP(cP); } }

    Introducir el parámetro del objeto

    Qué hace: Reemplaza un conjunto de parámetros con una clase, y actualiza todas las llamadas al método para pasar una instancia de la nueva clase como el valor pasado por parámetro.

    Dónde está disponible: Métodos

    Un ejemplo: Vamos a fijarnos en la clase MisSitios.

    import net.mauriciomatamala.eclipse.sitio.SitioConImagenes; import net.mauriciomatamala.eclipse.sitio.Ubicacion; public class MisSitios { private static Sitios sitios; private static SitioConImagenes crearSitio(int id, Ubicacion ubicacion, String calle, String piso, int numero, int CP, ArrayList imagenes){ Direccion direccion = new Direccion(ubicacion, calle, numero, piso, CP); SitioConImagenes nuevoSitio = new SitioConImagenes(id,direccion,imagenes); return nuevoSitio; } public static void main(String[] args) { sitios = new Sitios(); SitioConImagenes sitio1 = crearSitio(1,null,"C/Rue","3ºC",13,29004,null); SitioConImagenes sitio2 = crearSitio(2,null,"C/Pla","2ºB",11,29008,null); SitioConImagenes sitio3 = crearSitio(3,null,"C/Ruth","4ºC",3,29015,null); sitios.addSitio(sitio1); sitios.addSitio(sitio2); sitios.addSitio(sitio3); System.out.println("Los sitios almacenados son los siguientes:"); mostrarSitios(sitios); } private static void mostrarSitios(Sitios sitios) { for (int i = 0; i < sitios.size(); i++){ System.out.println("-----------------------"); Sitio sitio = sitios.getSitioPos(i); System.out.println("ID Sitio: " + sitio.getId()); Direccion direccion = sitio.getDireccion(); System.out.println("Calle: " + direccion.getCalle()); System.out.println("Número" + direccion.getNumero()); System.out.println("Piso:" + direccion.getPiso()); System.out.println("CP:" + direccion.getCP()); Ubicacion ubicacion = direccion.getUbicacion(); if (ubicacion != null){ System.out.println("Coordenadas: " + ubicacion.getLatitud() + "(Lat); " + ubicacion.getLongitud() + "(Long)"); System.out.println("Descripción: " + ubicacion.getDescripcion()); } } } }

    Si nos fijamos, el método crearSitio recibe como parámetro una dirección postal como un conjunto de parámetros sueltos, calle, numero, piso y CP. Imaginemos que deseamos reducir la signatura del método, de forma que en lugar de estos parámetros sueltos, reciba una única dirección postal.

    Ya existe una clase DireccionPostal en el paquete net.mauriciomatamala.eclipse.sitio que nos valdría para este menester. De todas formas, vamos a crear esta clase temporalmente dentro del paquete net.mauriciomatamala.eclipse.sitios, y posteriormente la eliminaremos para usar la clase DireccionPostal del paquete net.mauriciomatamala.eclipse.sitio.

    Si elegimos los parámetros destacados en amarillo en el siguiente código fuente, y aplicamos la factorización Introducir el parámetro del objeto, la clase MisSitos queda como sigue:

    package net.mauriciomatamala.eclipse.sitios; import java.util.ArrayList; import net.mauriciomatamala.eclipse.sitio.Direccion; import net.mauriciomatamala.eclipse.sitio.DireccionPostal; import net.mauriciomatamala.eclipse.sitio.Sitio; import net.mauriciomatamala.eclipse.sitio.SitioConImagenes; import net.mauriciomatamala.eclipse.sitio.Ubicacion; public class MisSitios { private static Sitios sitios; private static SitioConImagenes crearSitio(int id, Ubicacion ubicacion, DireccionPostal direccionPostal, ArrayList imagenes){ Direccion direccion = new Direccion(ubicacion, direccionPostal.getCalle(), direccionPostal.getNumero(), direccionPostal.getPiso(), direccionPostal.getCP()); SitioConImagenes nuevoSitio = new SitioConImagenes(id,direccion,imagenes); return nuevoSitio; } public static void main(String[] args) { sitios = new Sitios(); SitioConImagenes sitio1 = crearSitio(1,null,new DireccionPostal("C/Rue", "3ºC", 13, 29004),null); SitioConImagenes sitio2 = crearSitio(2,null,new DireccionPostal("C/Pla", "2ºB", 11, 29008),null); SitioConImagenes sitio3 = crearSitio(3,null,new DireccionPostal("C/Ruth", "4ºC", 3, 29015),null); sitios.addSitio(sitio1); sitios.addSitio(sitio2); sitios.addSitio(sitio3); System.out.println("Los sitios almacenados son los siguientes:"); mostrarSitios(sitios); } private static void mostrarSitios(Sitios sitios) { for (int i = 0; i < sitios.size(); i++){ System.out.println("-----------------------"); Sitio sitio = sitios.getSitioPos(i); System.out.println("ID Sitio: " + sitio.getId()); Direccion direccion = sitio.getDireccion(); System.out.println("Calle: " + direccion.getCalle()); System.out.println("Número" + direccion.getNumero()); System.out.println("Piso:" + direccion.getPiso()); System.out.println("CP:" + direccion.getCP()); Ubicacion ubicacion = direccion.getUbicacion(); if (ubicacion != null){ System.out.println("Coordenadas: " + ubicacion.getLatitud() + "(Lat); " + ubicacion.getLongitud() + "(Long)"); System.out.println("Descripción: " + ubicacion.getDescripcion()); } } } }

    Después de esto, deberemos borrar la clase DireccionPostal recien creada del paquete net.mauriciomatamala.eclipse.sitios, y hacer algunos cambios para que la clase DireccionPostal empleada sea la del paquete net.mauriciomatamala.eclipse.sitio.

    Introducir direccionamiento indirecto

    Qué hace: Crea un método que delega hacia el método seleccionado.

    Dónde está disponible: Métodos y/o llamadas a métodos.

    Un ejemplo: En ocasiones puede interesarnos poner el mismo nombre a métodos similares con distinto nombre de clases distintas. Para este ejemplo, vamos a suponer un proyecto llamado net.seragul.gestionpersonal donde hay una clase llamada Persona y otra clase llamada Empresa. Supongamos además que su código es el siguiente:

    //ARCHIVO: Empresa.java package net.seragul.GestionPersonal.items; public class Empresa extends Item { String CIF; public Empresa(String nombre, String cIF, String descripcion) { super(nombre,descripcion); CIF = cIF; } public String getCIF() { return CIF; } public void setCIF(String cIF) { CIF = cIF; } @Override public int getTipo() { return Item.ITEM_EMPRESA; } public String cadena() { return "Empresa: " + nombre + " CIF: " + CIF + " Descripcion: " + descripcion; } } ---------------------------------------------- // ARCHIVO: Persona.java package net.seragul.GestionPersonal.items; public class Persona extends Item{ String DNI, cargo; public Persona(String nombre, String dNI, String cargo, String descripcion) { super(nombre,descripcion); DNI = dNI; this.cargo = cargo; } public String getDNI() { return DNI; } public void setDNI(String dNI) { DNI = dNI; } public String getCargo() { return cargo; } public void setCargo(String cargo) { this.cargo = cargo; } @Override public int getTipo() { // TODO Apéndice de método generado automáticamente return Item.ITEM_PERSONA; } public String toString(){ return "Persona: " + nombre + " DNI: " + DNI + " Cargo: " + cargo + " Descripcion: " + descripcion; } } ------------------------------------------------------- // ARCHIVO: Item.java package net.seragul.GestionPersonal.items; public abstract class Item { protected String nombre; protected String descripcion; public static final int ITEM_PERSONA = 0; public static final int ITEM_EMPRESA = 1; public Item(String nombre, String descripcion) { this.nombre = nombre; this.descripcion = descripcion; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getDescripcion() { return descripcion; } public void setDescripcion(String descripcion) { this.descripcion = descripcion; } public abstract int getTipo(); } ---------------------------------------------------------- // ARCHIVO: DAO.java package controlador; import java.util.ArrayList; import net.seragul.GestionPersonal.items.Item; public class DAO { ArrayList<Item> items; public DAO(){ items = new ArrayList<Item>(); } public void insertarItem(Item item){ items.add(item); } public Item getItemPos(int pos){ if (pos < items.size()) return items.get(pos); else return null; } public int getSize(){ return items.size(); } } ----------------------------------------------------------- // ARCHIVO: InformeItems.java package vista; import controlador.DAO; import net.seragul.GestionPersonal.items.Empresa; import net.seragul.GestionPersonal.items.Item; import net.seragul.GestionPersonal.items.Persona; public class InformeItems { DAO db; public InformeItems(DAO db){ this.db = db; } public String generarInforme(){ String informe = ""; for (int i=0; i < db.getSize(); i++){ Item item = db.getItemPos(i); informe = informe + generarInformeItem(item); } return informe; } private String generarInformeItem(Item item) { if (item.getTipo() == Item.ITEM_EMPRESA){ Empresa empresa = (Empresa) item; return empresa.cadena(); } else if (item.getTipo() == Item.ITEM_PERSONA) { Persona persona = (Persona) item; return persona.toString(); } else return ""; } }

    Como podemos ver en el archivo InformeItems (en el código anterior destacada en blanco), las clases Persona y Empresa tienen métodos diferentes para mostrar su contenido en una cadena de texto formateada: cadena() y toString().

    Aunque existen mejores formas de hacer esto, es suficiente para ilustrar esta factorización. Vamos a aplicarla sobre el método cadena() de la clase Empresa. Tras aplicarla, la clase quedará del siguiente modo:

    // ARCHIVO: Empresa.java package net.seragul.GestionPersonal.items; public class Empresa extends Item { String CIF; public Empresa(String nombre, String cIF, String descripcion) { super(nombre,descripcion); CIF = cIF; } public String getCIF() { return CIF; } public void setCIF(String cIF) { CIF = cIF; } @Override public int getTipo() { return Item.ITEM_EMPRESA; } public String cadena() { return "Empresa: " + nombre + " CIF: " + CIF + " Descripcion: " + descripcion; } public static String toString(Empresa empresa) { return empresa.cadena(); } } --------------------------------------------------------- // ARCHIVO: InformeItems.java package vista; import controlador.DAO; import net.seragul.GestionPersonal.items.Empresa; import net.seragul.GestionPersonal.items.Item; import net.seragul.GestionPersonal.items.Persona; public class InformeItems { DAO db; public InformeItems(DAO db){ this.db = db; } public String generarInforme(){ String informe = ""; for (int i=0; i < db.getSize(); i++){ Item item = db.getItemPos(i); informe = informe + generarInformeItem(item); } return informe; } private String generarInformeItem(Item item) { if (item.getTipo() == Item.ITEM_EMPRESA){ Empresa empresa = (Empresa) item; // La siguiente línea ha cambiado automáticamente return Empresa.toString(empresa); } else if (item.getTipo() == Item.ITEM_PERSONA) { Persona persona = (Persona) item; return persona.toString(); } else return ""; } }

    Introducir fábrica

    Qué hace: crea un método "Fabric" que sustituye al método constructor. Todas las referencias al constructor serán sustituidas por el nuevo método.

    Dónde está disponible: métodos constructores

    Un ejemplo: Podemos crear un método de este tipo para los métodos constructores de Empresa o Persona.

    Para este patrón, probablemente hace falta una explicación algo más extensa. Para ello, vamos a apoyarnos en el patrón "Singleton". Descarga el siguiente ejemplo: AgendaSimple

    Primero vamos a introducir un método "Fabric" (o también llamado "Builder") sobre el constructor de la clase Agenda. Este método deberá llamarse getSingletonAgenda.

    Lo siguiente, es añadir en la clase Agenda un nuevo atributo llamado singletonAgenda, de tipo Agenda (sí, sí, de tipo Agenda dentro de la clase Agenda).

    Finalmente añadimos algo de código en el método Fabric:

    if (singletonAgenda == null){ singletonAgenda = new Agenda(); } return singletonAgenda;

    Es decir, la clase Agenda, quedaría así

    import java.util.ArrayList; import java.util.Iterator; public class Agenda { private static Agenda singletonAgenda; public static Agenda getSingletonAgenda() { if (singletonAgenda == null){ singletonAgenda = new Agenda(); } return singletonAgenda; } private ArrayList eventos; private Agenda(){ eventos = new ArrayList(); } public String getEvento(int pos) { return eventos.get(pos); } public void addEvento(String evento) { eventos.add(evento); } public void removeEvento(int pos){ this.eventos.remove(pos); } @Override public String toString() { Iterator iteradorEventos = this.eventos.iterator(); String listadoEventos = "EVENTOS EN LA AGENDA: \n"; while (iteradorEventos.hasNext()){ String evento = iteradorEventos.next(); listadoEventos += "Evento: " + evento + "\n"; } return listadoEventos; } }

    Si probamos de nuevo el ejemplo, entenderemos un poco mejor cómo podemos utilizar este patrón de factorización.

    Introducir parámetro

    Qué hace: Reemplaza una expresión con una referencia a un nuevo parámetro, y actualiza todas las llamadas el método para pasar la expresión como un valor de dicho parámetro.

    Dónde está disponible: Expresiones.

    Un ejemplo: Si nos fijamos en el método generarInforme de la clase InformeItems, podemos ver lo siguiente:

    public String generarInforme(){ String informe = ""; for (int i=0; i < db.getSize(); i++){ Item item = db.getItemPos(i); informe = informe + generarInformeItem(item); } return informe; }

    Supongamos que queremos que la cadena inicial, informe, sea pasada por un parámetro del método. Si nos colocamos en la expresión "", y aplicamos esta factorización, obtendremos algo como esto:

    public String generarInforme(String textoInicial){ String informe = textoInicial; for (int i=0; i < db.getSize(); i++){ Item item = db.getItemPos(i); informe = informe + generarInformeItem(item); } return informe; }

    Además, cambiarán todas las llamadas al método generarInforme, para añadirle el nuevo parámetro, con el valor "". Esto podemos verlo muy claramente si añadimos la siguiente clase justo antes de aplicar el patrón:

    public class PeopleSoft { public static void main(String[] args) { DAO dao = new DAO(); dao.insertarItem(new Persona("Juan García","7654321A","Administrativo","Gestiona el papeleo")); dao.insertarItem(new Empresa("PapelGreen","1234567Z","Recicla papel")); InformeItems informeItems = new InformeItems(dao); informeItems.generarInforme(); } }

    Autoencapsular campo

    Qué hace: Sustituye todas las referencias a un campo por funciones get y set. Es algo que querremos hacer si se accede direcctamente a un parámetro de una clase.

    Dónde está disponible: Atributos de clase

    Un ejemplo: Imaginemos que la clase Item contiene un atributo público llamado id al que se accede directamente desde InformeItems.

    // ARCHIVO: Item.java package net.seragul.GestionPersonal.items; public abstract class Item { public int id; protected String nombre; protected String descripcion; public static final int ITEM_PERSONA = 0; public static final int ITEM_EMPRESA = 1; public Item(String nombre, String descripcion) { this.nombre = nombre; this.descripcion = descripcion; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getDescripcion() { return descripcion; } public void setDescripcion(String descripcion) { this.descripcion = descripcion; } public abstract int getTipo(); } ---------------------------------------------------- // ARCHIVO: InformeItems.java package vista; import controlador.DAO; import net.seragul.GestionPersonal.items.Empresa; import net.seragul.GestionPersonal.items.Item; import net.seragul.GestionPersonal.items.Persona; public class InformeItems { DAO db; public InformeItems(DAO db){ this.db = db; } public String generarInforme(String textoInicial){ String informe = textoInicial; for (int i=0; i < db.getSize(); i++){ Item item = db.getItemPos(i); informe = informe + generarInformeItem(item); } return informe; } private String generarInformeItem(Item item) { if (item.getTipo() == Item.ITEM_EMPRESA){ Empresa empresa = (Empresa) item; return "ID -- " + empresa.id + "\n" + empresa.toString(); } else if (item.getTipo() == Item.ITEM_PERSONA) { Persona persona = (Persona) item; return "ID -- " + persona.id + "\n" + persona.toString(); } else return ""; } }

    Si nos colocamos sobre la expresión empresa.id del método generarInformeItem de la clase InformeItems, y aplicamos esta factorización, se crearán los métodos getId() y setId() en la clase Item, y se sustituirá la expresión empresa.id y persona.id del método generarInformeItem por la expresión empresa.getId() y persona.getId() respectivamente.

    Generalizar tipo declarado

    Qué hace: Mermite cambiar una clase empleada en una declaración por una de sus superclases.

    Dónde está disponible: Referencias a clases, declaraciones de campos, variables locales y parámetros con referencia a tipo

    Inferir argumentos de tipo genérico

    Qué hace: La refactorización de Eclipse permite inferir los tipos correctos de los argumentos para clases. Se emplea para códigos antiguos que deben ser actualizados a versiones posteriores a Java 5.

    Dónde está disponible: Proyectos, paquetes y clases.

    Un ejemplo: Si tenemos una variable definida del siguiente modo:

    private final ConcurrentHashMap map = new ConcurrentHashMap();

    Tras aplicar esta factorización, quedaría del siguiente modo:

    private final ConcurrentHashMap<TimedKey, Object> map = new ConcurrentHashMap<TimedKey, Object>();

    Migrar archivo JAR

    Qué hace: Una tarea habitual cuando se mantiene un proyecto es tener que actualizar liberías que usa. Por ejemplo, podemos imaginar un proyecto llamado "GestionPersonal", que utiliza una librería llamada "GestionNominas", gracias a la cual simplifica la gestión de las nóminas del personal. Supongamos a su vez que el proyecto "GestionNominas" ha sido actualizado, de forma que la librería que se está usando en el proyecto "GestionPersonal" está obsoleta y debe ser actualizada. El proceso a seguir para hacer esto sin usar "Migrar archivo JAR" es el siguiente:

    1. Copiar el nuevo archivo JAR en la carpeta LIB del proyecto
    2. Borrar el anterior archivo JAR del "build path" en la opción sigiente: [Clic derecho sobre el proyecto]\Propiedades\Via de construcción de Java] y elegimos la pestaña Librerías.
    3. Añadimos el nuevo archivo JAR al "classpath" en dicha pestaña
    4. Renombramos el archivo JAR para que coincida con la versión del archivo JAR que se estaba usando hasta ahora

    En cambio empleando esta característica, todo resulta mucho más fácil. El proceso a seguir sería el siguiente:

    1. Navegamos en el explorador de paquetes hasta llegar al archivo JAR a acutalizar
    2. Hacemos clic derecho sobre el archivo JAR, y vamos al submenú Via de acceso de construcción\Migrar archivo JAR.
    3. En la ventana que aparece, seleccionamos el nuevo archivo JAR. Si deseamos que se conserve el nombre del archivo JAR existente (lo cual es bastante probable, ya que no deseamos romper las referencias al nombre del archivo), marcamos la casilla "sustituir contenido del archivo JAR pero conservar el nombre del archivo existente".

    Un ejemplo: Supongamos que contamos con un proyecto llamado "GestionNominas" con el siguiente código fuente (es un ejemplo muy corto, y poco probable como proyecto independiente, pero nos vale para ilustrar el problema.

    // ARCHIVO: Nomina.java package net.seragul.GestionNominas.modelo; import java.util.Date; import net.seragul.GestionPersonal.items.Empresa; import net.seragul.GestionPersonal.items.Persona; public class Nomina { Persona persona; Empresa empresa; int salario; Date fecha; public Nomina() { super(); } public Nomina(Persona persona, Empresa empresa, int salario, Date fecha) { super(); this.persona = persona; this.empresa = empresa; this.salario = salario; this.fecha = fecha; } public Persona getPersona() { return persona; } public void setPersona(Persona persona) { this.persona = persona; } public Empresa getEmpresa() { return empresa; } public void setEmpresa(Empresa empresa) { this.empresa = empresa; } public int getSalario() { return salario; } public void setSalario(int salario) { this.salario = salario; } public Date getFecha() { return fecha; } public void setFecha(Date fecha) { this.fecha = fecha; } }

    Como se puede ver, el código fuente de GestionNominas utiliza elementos del proyecto GestionPersonal. Por ello, vamos a importar el archivo JAR de GestionPersonal. Para ello seguimos el siguiente procedimiento:

    1. Hacemos clic derecho sobre el proyecto GestionNominas y elegimos la opción Propiedades\Vía de construcción de Java y elegimos la pestaña Bibliotecas.
    2. Hacemos clic en el botón Añadir JAR externos y seleccionamos el archivo JAR del proyecto GestionPersonal.

    Como podemos ver en el explorador de paquetes, en el proyecto GestionNominas aparece un nuevo apartado llamado Librerías referidas donde está el JAR añadido.

    Ahora supongamos que hemos modificado el proyecto GestionPersonal. Podemos hacer un cambio sencillo para ejemplificar el problema, añadiendo un nuevo atributo llamado referencia a la clase Item, quedando del siguiente modo:

    package net.seragul.GestionPersonal.items; public abstract class Item { private int id; protected String nombre; protected String referencia; protected String descripcion; public static final int ITEM_PERSONA = 0; public static final int ITEM_EMPRESA = 1; public Item(String nombre, String descripcion) { this.nombre = nombre; this.descripcion = descripcion; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getDescripcion() { return descripcion; } public void setDescripcion(String descripcion) { this.descripcion = descripcion; } public abstract int getTipo(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getReferencia() { return referencia; } public void setReferencia(String referencia) { this.referencia = referencia; } }

    Entonces, como deseamos actualizar el archivo JAR que se está utilizando en el proyecto GestionNominas, debemos generar un nuevo archivo JAR y seguir el proceso anteriormente indicado, o bien utilizar la opción Refactorizar\Migrar archivo JAR:

    Actividad 4. Crea un nuevo proyecto llamado net.ed.SaludoScrum, con una clase llamada Saludo. La clase Saludo tiene un método llamado saludar() que muestra por pantalla el siguiente mensaje:

    ################################################# # Hola, estás ejecutando Scrum # #################################################

    Tras crear el proyecto, expórtalo como SaludoScrum.jar.

    Importa el proyecto desde el proyecto net.ametsis.ed.Scrum y muestra el mensaje que muestra la clase Saludo.


    Toma una captura llamada Act4.1-eclipse.png donde se puedan ver las siguientes cosas:

    1. En el editor se ve como se utiliza la clase Saludo desde el método main del proyecto Scrum
    2. En la consola de Eclipse, se puede ver cómo el proyecto Scrum muestra el mensaje al ejecutarlo.
    3. Finalmente, también se debe ver en la captura la librería "SaludoScrum.jar" en el explorador de paquetes, como librería importada.

    Tras hacer esto, modifica el mensaje del proyecto net.ed.SaludoScrum, para que se muestre del siguiente modo:

    --------------------------------------------------- Inicio del programa Scrum ---------------------------------------------------

    En esta ocasión exporta el proyecto con el nombre SaludoScrum2.jar. Utiliza el procedimiento explicado para actualizar una librería Jar, para que el proyecto net.ametsis.ed.Scrum utilice la nueva versión del proyecto .net.ed.SaludoScrum, a través de la librería exportada.


    Deberás tomar dos capturas más. La primera de ellas, se llamará Act4.2-eclipse.png, donde debe poder verse el momento en que estás actualizando la librería.

    La segunda captura, llamada Act4.3-eclipse.png, será similar a la primera, es decir:

    1. En el editor se ve como se utiliza la clase Saludo desde el método main del proyecto Scrum
    2. En la consola de Eclipse, se puede ver cómo el proyecto Scrum muestra el mensaje actualizado al ejecutarlo.
    3. Finalmente, también se debe ver en la captura la librería "SaludoScrum.jar" en el explorador de paquetes, como librería importada.

    Refuerzo de refactorización

    Como repaso de todo lo visto, os dejo el siguiente Refuerzo de refactorización.

    Crear script

    Qué hace: Permite crear un script donde se pueden guardar las refactorizaciones deseadas realizadas sobre el código.

    Aplicar script

    Qué hace: Permite aplicar un script anteriormente creado.

    Historial

    Qué hace: Almacena todas las refactorizaciones realizadas sobre el código.

    Dependencias entre proyectos

    Eclipse permite indicar que un proyecto depende de otro. El procedimiento explicado a continuación solamente es aplicable en eclipse, ya que lo habitual es utilizando librerías JAR, como ya vimos anteriormente.

    1. Seleccionamos el proyecto de interés y hacemos clic derecho, y elegimos las propiedades.
    2. En la ventana que aparece, elemgimos la opción "Vía de construcción de Java", y una vez dentro elegimos la pestaña Proyectos
    3. Si hacemos clic sobre el botón Añadir, podemos elegir cualquier proyecto acutualmente definido en Eclipse. A partir de ese momento ya podremos utilizar sus clases como si perteneciesen a un archivo JAR importado.

    Usando librerías JAR en Eclipse

    En ocasiones es necesario distribuir una librería junto con un proyecto, aunque no esté añadida al classpath (es decir, no se está enlazando realmente, sino que simplemente está disponible con el proyecto). Para ejemplicar esto, vamos crear un proyecto nuevo, llamado GestionAlmacen. En principio el proyecto puede estar vacío. Una vez creado, vamos a seguir los siguientes pasos:

    1. Creamos una carpeta lib en el proyecto: clic derecho sobre el proyecto, Nueva\carpeta.
    2. Seleccionamos el menú Archivo\Importar\General\Sistema de archivos.
    3. Seleccionamos la carpeta donde se encuentra el archivo JAR, y después seleccionamos el archivo JAR mediante su casilla de verificación. También elegimos la carpeta destino dentro del proyecto del archivo JAR, en la entrada de texto de la carpeta.
    4. Hacemos clic en siguiente

    Tras esto, el archivo JAR aparecerá en la carpeta lib del proyecto (podemos verlo en el explorador de paquetes), aunque realmente no se esté usando en el proyecto. Podemos hacer la siguiente comprobación. Vamos a añadir el siguiente código fuente al proyecto:

    // ARCHIVO: Pedido.java package net.seragul.GestionAlmacen.modelo; import java.util.Date; public abstract class Pedido { int id; Date fecha; String articulo; public final static int PEDIDO_PERSONA = 0; public final static int PEDIDO_EMPRESA = 1; public Pedido() { super(); } public Pedido(int id, Date fecha, String articulo) { super(); this.id = id; this.fecha = fecha; this.articulo = articulo; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Date getFecha() { return fecha; } public void setFecha(Date fecha) { this.fecha = fecha; } public String getArticulo() { return articulo; } public void setArticulo(String articulo) { this.articulo = articulo; } public abstract int getType(); } --------------------------------------------------------------------------------- // ARCHIVO: PedidoPersona.java package net.seragul.GestionAlmacen.modelo; import java.util.Date; public class PedidoPersona extends Pedido { Persona persona; public PedidoPersona() { super(); } public PedidoPersona(int id, Date fecha, String articulo, Persona persona) { super(id, fecha, articulo); this.persona = persona; } public Persona getPersona() { return persona; } public void setPersona(Persona persona) { this.persona = persona; } @Override public int getType() { return PEDIDO_PERSONA; } } -------------------------------------------------------------------------------- // ARCHIVO: PedidoEmpresa.java package net.seragul.GestionAlmacen.modelo; import java.util.Date; public class PedidoEmpresa extends Pedido { Empresa empresa; public PedidoEmpresa() { super(); // TODO Apéndice de constructor generado automáticamente } public PedidoEmpresa(int id, Date fecha, String articulo, Empresa empresa) { super(id, fecha, articulo); this.empresa = empresa; } public Empresa getEmpresa() { return empresa; } public void setEmpresa(Empresa empresa) { this.empresa = empresa; } @Override public int getType() { // TODO Apéndice de método generado automáticamente return 0; } }

    Como se puede ver, estamos usando objetos del proyecto net.seragul.GestionPersonal. Eclipse genera errores donde aparecen dichas clases, porque no sabe dónde encontrarlas (a pesar de que la hemos incluido en una carpeta del proyecto). Para poder enlazar con las clases del archivo JAR, debemos hacer lo siguiente:

    1. Hacer clic derecho sobre el proyecto, y abrir la opción Propiedades\Via de construcción Java e ir hasta la pestaña Bibliotecas.
    2. Hacer clic en el botón "Añadir archivo JAR", y en la ventana que se abre desplegamos el proyecto net.seragul.GestionAlmacen hasta llegar a la carpeta lib.
    3. Hacemos clic sobre el archivo JAR.

    A partir de este momento, los errores asociados a las clases Persona y Empresa dentro del proyecto, nos ofrecen la posibilidad de solucionarlos importándolas del proyecto net.seragul.GestionPersonal.

    Javadoc y Eclipse

    La documentación de un proyecto pasa a ser algo vital cuando el proyecto crece. Javadoc es una tecnología de Oracle que permite crear comentarios parseables en el código fuente para poder generar automáticamente una documentación relacionada con el proyecto.

    Javadoc

    Javadoc es una utilidad de Oracle para la generación de documentación de APIs en formato HTML a partir de código fuente Java. Javadoc es el estándar para documentar clases de Java. La mayoría de los IDEs utilizan javadoc para generar de forma automática documentación de clases.

    La documentación que se puede aportar para una clase puede ser:

    • Nombre de la clase, descripción general, número de versión, nombre de autores.
    • Documentación de cada constructor o método incluyendo información como:
      • nombre del constructor o método
      • tipo de retorno
      • nombres y tipos de parámetros si los hay
      • descripción general
      • descripción de parámetros

    La documentación para Javadoc ha de incluirse entre símbolos de comentario que han de empezar con una barra y doble asterisco, y terminar con un asterisco y barra simple, como por ejemplo:

    /** * Esto es un comentario para Javadoc */

    Es importante la ubicación de un comentario Javadoc. si está incluido justo antes de la declaración de clase se considerará un comentario de clase, y si está incluido justo antes de la signatura de un constructor o método se considerará un comentario de ese constructor o método.

    Javadoc permite indicar ciertas etiquetas para destallar ciertos aspectos del elemento documentado. Observa el siguiente ejemplo:

    /** * Constructor con paso de atributos * * @param id Identificador único del pedido * @param fecha Fecha del pedido * @param articulo Descripción del artículo pedido */ public Pedido(int id, Date fecha, String articulo) { super(); this.id = id; this.fecha = fecha; this.articulo = articulo; }

    Como podemos ver, aparecen tres etiquetas @param, que indican qué significado tiene cada uno de los parámetros. Además de esta etiqueta, existen otras:

    Etiqueta Para qué sirve
    @author nombre_autor Nombre del autor o autores del código
    @deprecated descripción Indica que el método o clase es obsoleto y que no se recomienda su uso.
    @param nombre_parámetro descripción Definición de un parámetro de un método, es requerido para todos los parámetros del método.
    @return descripción Informa de lo que devuelve el método. No se aplica en constructores o métodos "void".
    @see referencia* Asocia con otro método o clase.
    @throws Excepción lanzada por el método
    @version Versión del método o clase.
    {@link referencia* nombre_mostrado} Esta es otra etiqueta bastante usada. Sirve para enlazar con otro elemento de la documentación. Su uso es algo particular. Por ejemplo, imaginemos que estamos definiendo un parámetro de una función llamado persona, que es de tipo Persona. Por ello, además de decir que este parámetro contiene información sobre una persona, también vamos a crear un enlace a la clase Persona. Entonces la etiqueta sería como sigue: {@link Persona Persona}.

    Si deseamos ver más parámetros, podemos consultarlos en docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags

    Una referencia puede tener distintos niveles de precisión, dependiendo del caso. Por ejemplo:

    • Referencia a un método: #método()
    • Referencia a un método de una cierta clase: clase#método()
    • Referencia a una clase de un paquete: paquete.clase
    • Referencia a un método de una clase de un cierto paquete: paquete.clase#método()

    Así, la etiqueta {@link net.seragul.GestionPersonal.Persona#getId() getId()} hace referencia al método getId() de la clase Persona del paquete net.seragul.GestionPersonal.

    Un ejemplo de documentación Javadoc es el siguiente (sacado de Wikipedia):

    /** * Inserta un título en la clase descripción. * Al ser el título obligatorio, si es nulo o vacío se lanzará * una excepción. * * @param titulo El nuevo título de la descripción. * @throws IllegalArgumentException Si titulo es null, está vacío o contiene sólo espacios. */ public void setTitulo (String titulo) throws IllegalArgumentException { if (titulo == null || titulo.trim().equals("")) { throw new IllegalArgumentException("El título no puede ser nulo o vacío"); } else { this.titulo = titulo; } }

    Generar Javadoc con Eclipse

    Ahora supongamos que hemos documentado nuestro proyecto net.seragul.GestionAlmacen del siguiente modo con Javadoc:

    // ARCHIVO: Pedido.java /** * * Esta clase define un pedido genérico. Se trata de una clase abstracta * * @author Mauricio Matamala Peinado * @version 0.0 * @see <a href="http://www.mauriciomatamala.net">www.mauriciomatamala.net</a> * */ package net.seragul.GestionAlmacen.modelo; import java.util.Date; public abstract class Pedido { /** * Identificador único del pedido. */ int id; /** * Fecha del pedido. */ Date fecha; /** * Descripción textual del artículo. */ String articulo; /** * Constante para identificar la instancia como PedidoPersona. * {@link PedidoPersona#getType()} */ public final static int PEDIDO_PERSONA = 0; /** * Constante para identificar la instancia como PedidoEmpresa. * {@link PedidoEmpresa#getType()} */ public final static int PEDIDO_EMPRESA = 1; /** * Constructor */ public Pedido() { super(); } /** * Constructor con paso de atributos * * @param id Identificador único del pedido * @param fecha Fecha del pedido * @param articulo Descripción del artículo pedido */ public Pedido(int id, Date fecha, String articulo) { super(); this.id = id; this.fecha = fecha; this.articulo = articulo; } /** * @return Identificador del pedido */ public int getId() { return id; } /** * @param id Contiene el identificador del pedido. */ public void setId(int id) { this.id = id; } /** * @return El va */ public Date getFecha() { return fecha; } /** * * @param fecha Fecha en que se realizó el pedido */ public void setFecha(Date fecha) { this.fecha = fecha; } /** * @return Atributo <em>articulo</em> */ public String getArticulo() { return articulo; } /** * @param articulo Descripción textual del artículo del pedido */ public void setArticulo(String articulo) { this.articulo = articulo; } /** * @return Valor que indica si la instancia es de tipo {@link PedidoPersona} o bien {@link PedidoEmpresa} * {@link Pedido#PEDIDO_EMPRESA} * {@link Pedido#PEDIDO_PERSONA} */ public abstract int getType(); } ---------------------------------------- // ARCHIVO: PedidoEmpresa.java package net.seragul.GestionAlmacen.modelo; /** * Esta clase define un pedido realizado por una empresa. Es descendiente de la clase Pedido * * @author Mauricio Matamala Peinado * @version 0.0 * @see <a href="http://www.mauriciomatamala.net">www.mauriciomatamala.net</a> */ import java.util.Date; import net.seragul.GestionPersonal.items.Empresa; public class PedidoEmpresa extends Pedido { /** * Empresa que realiza el pedido. * */ Empresa empresa; /** * Constructor */ public PedidoEmpresa() { super(); } /** * Constructor con atributos. Se emplea el constructor de la superclase {@link Pedido}. Para concer * el significado de los atributos <em>id</em>, <em>fecha</em> y <em>articulo</em> remitirse a {@link Pedido} * * @param id atributo de superclase {@link Pedido#id} * @param fecha atributo de superclase {@link Pedido#fecha} * @param articulo atributo de superclase {@link Pedido#articulo} * @param empresa {@link Empresa} que realiza el pedido. */ public PedidoEmpresa(int id, Date fecha, String articulo, Empresa empresa) { super(id, fecha, articulo); this.empresa = empresa; } /** * * @return Empresa que realizó el pedido */ public Empresa getEmpresa() { return empresa; } /** * * @param empresa Asigna un valor al atributo {@link PedidoEmpresa#empresa} */ public void setEmpresa(Empresa empresa) { this.empresa = empresa; } /** * @return Devuelve la constante {@link Pedido#PEDIDO_EMPRESA} */ @Override public int getType() { return Pedido.PEDIDO_EMPRESA; } } ---------------------------------------- // ARCHIVO: PedidoPersona.java package net.seragul.GestionAlmacen.modelo; /** * Esta clase define un pedido realizado por una persona. Es descendiente de la clase Pedido * * @author Mauricio Matamala Peinado * @version 0.0 * @see <a href="http://www.mauriciomatamala.net">www.mauriciomatamala.net</a> */ import java.util.Date; import net.seragul.GestionPersonal.items.Persona; public class PedidoPersona extends Pedido { /** * Persona que realiza el pedido. * */ Persona persona; /** * Constructor */ public PedidoPersona() { super(); } /** * Constructor con atributos. Se emplea el constructor de la superclase {@link Pedido}. Para concer * el significado de los atributos <em>id</em>, <em>fecha</em> y <em>articulo</em> remitirse a {@link Pedido} * * @param id atributo de superclase {@link Pedido#id} * @param fecha atributo de superclase {@link Pedido#fecha} * @param articulo atributo de superclase {@link Pedido#articulo} * @param persona {@link Persona} que realiza el pedido. * */ public PedidoPersona(int id, Date fecha, String articulo, Persona persona) { super(id, fecha, articulo); this.persona = persona; } /** * * @return Empresa que realizó el pedido */ public Persona getPersona() { return persona; } /** * * @param persona Asigna un valor al atributo {@link PedidoPersona#persona} */ public void setPersona(Persona persona) { this.persona = persona; } /** * @return Devuelve la constante {@link Pedido#PEDIDO_PERSONA} */ @Override public int getType() { return PEDIDO_PERSONA; } }

    Para generar la documentación, debemos seleccionar el proyecto en cuestión, en este caso net.seraguo.GestionAlmacen, y luego elegir el menú Proyecto\Generar Javadoc. Entonces veremos una ventana como la siguiente:

    En la entrada de texto "Mandato de texto", debemos indicar la ruta al programa javadoc.exe de la versión de Java que tengamos instalado.

    Debemos también marcar el proyecto que se desea documentar.

    Por último, en la entrada de texto "destino" escribimos la ruta donde queremos que se almacene la documentación generada. Lo lógico es hacerlo dentro de la misma carpeta del proyecto. Podremos ver que se genera una serie de documentación en la carpeta especificada, empezando por una página llamada index.html

    Actividad 5. Descarga el proyecto Sitios.zip y documéntalo.

    Entrega la documentación generada en un archivo llamado Act5-eclipse.zip.

    Actualizaciones e instalación de plug-ins

    Eclipse permite instalar actualizaciones y nuevos componentes a través del gestor de actualizaciones. Los componentes en Eclipse son llamados plug-ins.

    OJO. Si estamos tras un proxy, debemos configurarlo en el menú Ventana\Preferencias\General\Conexiones de red. De otro modo, Eclipse podría no ser capaz de descargar las actualizaciones.

    Actualizaciones

    Para actualizar Eclipse, elegimos el menú Ayuda\Check for updates.

    Instalar un plug-in

    Para instalar un plugin, elegimos el menú Ayuda\Install new software. Cuando intentamos instalar un plug-in, Eclipse espera que le facilitemos la URL de un sitio que almacente componenetes para Eclipse. Los siguientes son ejemplos de repositorios oficiales de Eclipse:

    Al proporcionar dicha URL, podremos ver diferentes componentes disponibles:

    Si lo que queremos es ver los componentes que ya están instalados, podemos ir al menú Ayuda\Installation details, y ver el software instalado en la pestaña Installed software

    Eclipse Marketplace

    Existe otra forma de instalar software. El inconveniente que tiene el "Update Manager" es que requiere conocer la URL del sitio desde donde se instalará el software. Otra forma de instalar software es desde el Marketplace, donde podemos ver descripciones y puntuaciones de usuarios. Las nuevas versiones de Eclipse traen preinstalado el cliente de Marketplace, pero en versiones anteriores había que instalar el "Marketplace client" desde el Update Manager.

    Para acceder al Marketplace, hacemos clic en el menú Ayuda\Eclipse Marketplace. Por ejemplo, otra herramienta que podríamos utilizar para el diseño de un proyecto es UML Designer.

    Actividad 6. Infórmate sobre el plugin Eclipse Color Theme. Instálalo y utilízalo.

    Toma una captura donde se pueda observar como utilizas el plugin, y guárdala con el nombre Act6-eclipse.png.

    Preferencias de desarrollo de Eclipse

    Algunas opciones interesantes que podemos encontrar de cara a la escritura de código son:

    • Escritura automática de punto y coma: Opción ubicada en Ventana\Preferencias\Java\Fuente CSS\Escritura. Con esta opción, podemos escribir ";" en medio de un sentencia, que Eclipse la colocará correctamente.
    • Escape de texto al pegar en un literal de tipo serie: Opción ubicada en Ventana\Preferencias\Java\Fuente CSS\Escritura\En literales de serie . Con esta opción se puede "escapar" automáticamente el texto pegado en una cadena de texto (String). Por ejemplo, podemos copiar un codigo HTML en una cadena de texto y Eclipse "escapará" el texto automáticamente.
    • Si por ejemplo pegamos el texto <a href="tutorials/index.html">Tutorials</a> dentro de la expresión s = "";, el resultado será s = "<a href=\"tutorials/index.html\">Tutorials</a>";

    • Habilitar autoactivación automática: Opción ubicada en Ventana\Preferencias\Java\Fuentes CSS\Asistencia de contenido\Autoactivación. Esta opción permite que el asistente de contenido de abra automáticamente al escribir un cierto carácter. Por defecto, ese carácter es ".". Esto quiere decir que si escribimos System. el asistente nos ofrecerá las opciones disponibles, sin necesidad de pulsar Ctrl+Spacio.
    • Habilitar autoactivación automática (con cualquier tecla pulsada): Esta opción es la misma que la anterior. Lo único que cambiamos es el contenido de la entrada de texto Desencadenantes para activación automática para Java, que incluirá la cadena siguiente: .abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. De este modo, escribamos lo que escribamos, el asistente nos dará las opciones disponibles.
    • Enable annotation-based null analysis: Opción ubicada en Ventana\Preferencias\Java\Compilador\Errores/Avisos\Null analysis. Esta opción permite hacer anotaciones de tipo @NonNull o @Nullable en el código.

      Las anotaciones permiten que nuestro código sea más claro y además Eclipse las utiliza para ayudar al programador a crear mejor código fuente. Por ejemplo, vamos a fijarnos en la clase Pedido:

      /** * * Esta clase define un pedido genérico. Se trata de una clase abstracta * * @author Mauricio Matamala Peinado * @version 0.0 * @see www.mauriciomatamala.net * */ package net.seragul.GestionAlmacen.modelo; import java.util.Date; import org.eclipse.jdt.annotation.*; public abstract class Pedido { /** * Identificador único del pedido. */ int id; /** * Fecha del pedido. */ Date fecha; /** * Descripción textual del artículo. */ String articulo; /** * Constante para identificar la instancia como PedidoPersona. * {@link PedidoPersona#getType()} */ public final static int PEDIDO_PERSONA = 0; /** * Constante para identificar la instancia como PedidoEmpresa. * {@link PedidoEmpresa#getType()} */ public final static int PEDIDO_EMPRESA = 1; /** * Constructor */ public Pedido() { super(); } /** * Constructor con paso de atributos * * @param id Identificador único del pedido * @param fecha Fecha del pedido * @param articulo Descripción del artículo pedido */ public Pedido(int id, Date fecha, String articulo) { super(); this.id = id; this.fecha = fecha; this.articulo = articulo; } /** * @return Identificador del pedido */ public int getId() { return id; } /** * @param id Contiene el identificador del pedido. */ public void setId(int id) { this.id = id; } /** * @return El va */ public Date getFecha() { return fecha; } /** * * @param fecha Fecha en que se realizó el pedido */ public void setFecha(@NonNull Date fecha) { this.fecha = fecha; } /** * @return Atributo <em>articulo</em> */ public String getArticulo() { return articulo; } /** * @param articulo Descripción textual del artículo del pedido */ public void setArticulo(String articulo) { this.articulo = articulo; } /** * @return Valor que indica si la instancia es de tipo {@link PedidoPersona} o bien {@link PedidoEmpresa} * {@link Pedido#PEDIDO_EMPRESA} * {@link Pedido#PEDIDO_PERSONA} */ public abstract int getType(); }

      Si nos fijamos en el texto destacado en blanco, hay una anotación @NonNull. Esto significa que si se utiliza este método desde alguna parte del código, Eclipse nos avisará si no hemos comprobado que el parámetro pasado al método no es null. Por ejemplo, vamos a suponer la siguiente clase:

      package net.seragul.GestionAlmacen.controlador; import java.util.Date; import net.seragul.GestionAlmacen.modelo.*; import net.seragul.GestionPersonal.items.Empresa; public class controller { private static Empresa asignarValoresEmpresa(int id, String CIF, String nombre, String descripcion, String referencia){ Empresa empresa = new Empresa(); empresa.setId(id); empresa.setCIF(CIF); empresa.setNombre(nombre); empresa.setDescripcion(descripcion); empresa.setReferencia(referencia); return empresa; } private static PedidoEmpresa asignarValoresPedidoEmpresa(int id, Date fecha, String articulo, Empresa empresa){ PedidoEmpresa pedidoEmpresa = new PedidoEmpresa(); pedidoEmpresa.setId(id); pedidoEmpresa.setFecha(fecha); pedidoEmpresa.setArticulo(articulo); pedidoEmpresa.setEmpresa(empresa); return pedidoEmpresa; } public static void main(String[] args) { Empresa empresa = asignarValoresEmpresa(1,"55533322f","Seragul S.A.","Empresa de venta de material fungible","SER01"); PedidoEmpresa pedidoEmpresa = asignarValoresPedidoEmpresa(1,new Date(),"Funda de tableta 10.1",empresa); } }

      Si nos fijamos en la siguiente imagen, podremos ver que se muestra una advertencia:

      La razón es que Eclipse ha encontrado una anotación @NonNull en el método setFecha y no estamos comprobando explíctamente que la fecha no sea null. Una solución a este error sería añadir, por ejemplo, el siguiente código:

      package net.seragul.GestionAlmacen.controlador; import java.util.Date; import net.seragul.GestionAlmacen.modelo.*; import net.seragul.GestionPersonal.items.Empresa; public class controller { private static Empresa asignarValoresEmpresa(int id, String CIF, String nombre, String descripcion, String referencia){ Empresa empresa = new Empresa(); empresa.setId(id); empresa.setCIF(CIF); empresa.setNombre(nombre); empresa.setDescripcion(descripcion); empresa.setReferencia(referencia); return empresa; } private static PedidoEmpresa asignarValoresPedidoEmpresa(int id, Date fecha, String articulo, Empresa empresa){ PedidoEmpresa pedidoEmpresa = new PedidoEmpresa(); pedidoEmpresa.setId(id); if (fecha != null) pedidoEmpresa.setFecha(fecha); else pedidoEmpresa.setFecha(new Date()); pedidoEmpresa.setArticulo(articulo); pedidoEmpresa.setEmpresa(empresa); return pedidoEmpresa; } public static void main(String[] args) { Empresa empresa = asignarValoresEmpresa(1,"55533322f","Seragul S.A.","Empresa de venta de material fungible","SER01"); PedidoEmpresa pedidoEmpresa = asignarValoresPedidoEmpresa(1,new Date(),"Funda de tableta 10.1",empresa); } }

      Para todo esto funcione, es preciso que nuestro proyecto emplee la librería que contiene todas estas anotaciones. Dicha librería (si estamos usando Java 8) es org.eclipse.jdt.annotation-2.0.0.jar. Debemos descargarla y añadirla al "classpath" del proyecto.

      Además de la anotación @NonNull, contamos con otras dos:

      • @NonNull: un nulo no es un valor permitido.
      • @Nullable: se permite y se debe esperar un valor nulo.
      • @NonNullByDefault: los tipos en las signaturas de métodos que carezcan de una anotación de nulos se consideran como no nulos.

      Se da soporte a las anotaciones @NonNull y @Nullable en estas ubicaciones:

      • Parámetro de método
      • Retorno de método (se utiliza en la declaración del método)
      • Variables locales

      Se da soporte a @NonNullByDefault para:

      • Métodos: con el fin de que afecten a todos los tipos de la firma de ese método. Se coloca encima de la declaración de la función.
      • Tipos (clases, interfaces, enumeraciones): con el fin de que afecten a todos los métodos del cuerpo de tipo.
      • Paquete (a través de un archivo package-info.java): con el fin de que afecte a todos los tipos del paquete.

      ¡OJO! Para que estas anotaciones sean utilizadas por Eclipse, es preciso configurar su uso en Ventana\Preferencias\Java\Compilador\Errores/Avisos\Null analysis

      Actividad 7. Partimos del proyecto Scrum. No podemos permitir que se añada una Tarea null a una Historia de usuario en el proyecto Scrum. Por ello, asegúrate de que Eclipse nos avise cuando no se hagan comprobaciones sobre la Tarea que se está insertando en HistoriaUsuario.

      Para comprobar que el análisis se está haciendo correctamente, harás lo siguiente:

      • Declara una nueva Tarea sin asignarle ningún valor.
      • Añade la tarea anteriormente creada la historia de usuario historia1

      Toma una captura donde se pueda apreciar cómo Eclipse te informa de que no puedes insertar tareas nulas en HistoriaUsuairo y guárdala con el nombre Act7-eclipse.png.

      Limpieza del código fuente

      Eclipse permite realizar una limpieza de código, lo que incluye cosas como eliminar espacios innecesarios, optimizar partes del código, añadir anotaciones (como @Override o @Deprecated), etc. Para acceder a esta utilidad deber acceder a Código fuente\Limpiar. La ventana que se abre, nos permite usar un perfil preconfigurado, o bien usar un perfil personalizado. Es preciso prestar atención al tipo de limpieza que se va a hacer.

      Si queremos ver el perfil preconfigurado, debemos ir hasta Ventana\Preferencias\Java\Estilo de código\Limpiar, y editar el perfil interno de Eclipse, o bien crear uno nuevo según nuestras preferencias.

      Exportar/Importar preferencias de Eclipse

      Si necesitamos trabajar en un entorno recién instalado, o que no es el que solemos usar, podemos exportar/importar la configuración de nuestras preferencias. Para ello vamos a la opción Archivo\Exportar\General\Preferencias. Para importar, el proceso es el mismo, pero con la opción Archivo\Importar\General\Preferencias

      Podemos utilizar preferencias prediseñadas para cambiar ciertos aspectos del entorno. Por ejemplo, en http://eclipsecolorthemes.org/ podemos encontrar temas prediseñados para los colores del entorno gráfico sin tener que cambiar aspecto a aspecto manualmente. Necesitaríamos descargar el archivo de preferencias, e importarlas.

      Plantillas

      En Eclipse podemos crear plantillas para fragmentos de código. Por ejemplo, supongamos que creamos frecuentemente un método del tipo public void metodo(){}. En lugar de escribir una y otra vez lo mismo, podemos crear una plantilla para escribir pvm y que eso se transforme en el fragmento anterior. Para crear plantillas, debemos ir a Ventana\Preferencias\Java\Fuentes CSS\Plantillas. Como veremos hay muchas plantillas predefinidas. Vamos a centrarnos en una en concreto: for

      Si observamos veremos que hay tres plantillas para for:

      // PLANTILLA 1: for (int ${index} = 0; ${index} < ${array}.length; ${index}++) { ${line_selection}${cursor} } // PLANTILLA 2: for (int ${index} = 0; ${index} < ${array}.length; ${index}++) { ${array_type} ${array_element} = ${array}[${index}]; ${cursor} } // PLANTILLA 3: for (${iteratorType:newType(java.util.Iterator)} ${iterator} = ${collection}.iterator(); ${iterator}.hasNext(); ) { ${type:elemType(collection)} ${name:newName(type)} = (${type}) ${iterator}.next(); ${cursor} }

      Se puede observar que las tres plantillas emplean variabes como ${cursor}, ${line_selection}, etc. Podemos encontrar una referencia al significado de dichas variables en http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Fconcepts%2Fconcept-template-variables.htm. Nosotros por nuestra parte, vamos a poner un ejemplo para las plantillas anteriores. Antes vamos a aclarar qué significan las variables que aparecen:

      Plantilla FOR "Iterar sobre matriz"

      • ${index} Es una abreviatura de ${index:newName(int)}, que crea una nueva variable de int. Eclipse se asegura que no haya ninguna variable usada con ese nombre, y en caso contrario busca otro nombre.
      • ${array} Se reemplaza por un array existente en el mísmo ámbito donde se encuentra el bucle.
      • ${line_selection} Si seleccionamos un fragmento de código, y a continuación escribimos for (con lo que lo que estaba seleccionado se borra) y pulsamos Ctrl+Espacio, el texto que seleccionamos previamente queda dentro del bucle.
      • ${cursor} Indica dónde queremos que quede ubicado el cursor una vez que la plantilla se ha aplicado

      Vamos a ver un ejemplo. Escribimos el siguiente código, y después seleccionamos el código que aparece marcado en blanco:

      public void pruebaTemplate(){ long[] arrayIds = new long[5]; arrayIds[0]=1234567; arrayIds[0]=7123456; arrayIds[0]=6712345; arrayIds[0]=5671234; arrayIds[4]=4567123; System.out.println("Elemento número " + arrayIds[i]); }

      Una vez hecho esto, pulsamos las teclas Ctrl+Espacio, de mode que aparece el asistente de contenido. Entre las opciones disponibles, veremos que no aparece for. Es en este momento cuando debemos escribir for (no te preocupes si machacas el texto seleccionado). Ahora sí aparece la opción for - Iterar sobre matriz en el asistente de contenido. Elegimos dicha opción y el resultado será el siguiente:

      public void pruebaTemplate(){ long[] arrayIds = new long[5]; arrayIds[0]=1234567; arrayIds[0]=7123456; arrayIds[0]=6712345; arrayIds[0]=5671234; arrayIds[4]=4567123; for (int i = 0; i < arrayIds.length; i++) { System.out.println("Elemento número " + arrayIds[i]); } }

      Plantilla FOR "Iterar sobre matriz con variable temporal"

      Además de las variables comentadas en el apartado anterior, también tenemos las siguientes:

      • ${array_type} Es una abreviatura de ${array_type:elemType(array)}, que lo que viene a decir es "escribir el tipo del elemento de tipo array que se está usando en la plantilla". Así si el array usado en la plantilla es de tipo long, entonces la variable ${array_type} será sustituida por long.
      • ${array_element} Es una abreviatura de ${array_element:newName(array)}, que lo que viene a decir es "crear un nombre de una variable local nueva para un elemento del array que se está usando en la plantilla".

      Esta opción es muy parecida a la anterior, solo que no admite selección de texto. El ejemplo que vamos a utilizar es muy similar. Empecemos por el siguiente código:

      public void pruebaTemplate(){ long[] arrayIds = new long[5]; arrayIds[0]=1234567; arrayIds[0]=7123456; arrayIds[0]=6712345; arrayIds[0]=5671234; arrayIds[4]=4567123; }

      Ahora solo necesitamos escribir la palabra for y pulsar Ctrl+Espacio. Después, en el asistente de contenido elegimos la opción "for-iterar sobre matriz con variable temporal". El resultado será el siguiente:

      public void pruebaTemplate(){ long[] arrayIds = new long[5]; arrayIds[0]=1234567; arrayIds[0]=7123456; arrayIds[0]=6712345; arrayIds[0]=5671234; arrayIds[4]=4567123; for (int i = 0; i < arrayIds.length; i++) { long l = arrayIds[i]; } }

      Plantilla FOR "Iterar sobre colección"

      • ${iteratorType:newType(java.util.Iterator)} escribe el nombre de un tipo según lo indicado en el nombre cualificado (en este caso java.util.Iterator). Si el tipo no ha sido importado aún, lo importa en caso de que no cree conflictos, y en caso contrario escribe el nombre completamente cualificado.
      • ${collection} Es una abreviatura de ${collection:localVar(java.util.Collection)}, que lo que hace es buscar una colección declarada en el ámbito donde se va a ubicar el bucle for.
      • ${iterator} Es una abreviatura de ${iterator:newName(java.util.Iterator)}, que escribe un nuevo nombre de tipo java.util.Iterator que no exista en el ámbito donde se encuentra el bucle for.
      • ${type:elemType(collection)} Escribe el tipo de la colección utilizada en la plantilla.
      • ${name:newName(type)} Crea un nuevo nombre de variable del tipo definido anteriormente (${type:elemType(collection)} que no entre en conflicto con otro elemento definido en el ámbito del for.
      • ${type} Hace referencia al tipo definido anteriormente (en este caso ${type:elemType(collection)}).

      Para ejemplicar esta plantilla vamos a partir del siguiente código fuente:

      public void pruebaTemplate(){ ArrayList<String> listaCadenas = new ArrayList(); listaCadenas.add("Cadena 1"); listaCadenas.add("Cadena 2"); listaCadenas.add("Cadena 3"); }

      A continuación, escribimos for y pulsamos Ctrl+Espacio. El resultado debería ser como el siguiente:

      public void pruebaTemplate(){ ArrayList<String> listaCadenas = new ArrayList(); listaCadenas.add("Cadena 1"); listaCadenas.add("Cadena 2"); listaCadenas.add("Cadena 3"); for (Iterator iterator = listaCadenas.iterator(); iterator.hasNext();) { String string = (String) iterator.next(); } }

      Creación de plantillas personalizadas

      Además de utilizar plantillas ya existentes, lo bueno es que nosotros podemos crearnos nuestras propias plantillas, además de modificar las existentes. Para ello no tenemos más que añadir una nueva desde la opción de Plantillas, asignarle un nombre, descripción y elegir el código que queremos que se muestre al seleccionar la misma. Vamos a ver un ejemplo con una figura muy habitual en programación: el patrón de diseño Singleton. El patrón de diseño singleton (instancia única) está diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto. Su intención consiste en garantizar que una clase sólo tenga una instancia y proporcionar un punto de acceso global a ella. Por ejemplo, el siguiente código corresponde a una clase de tipo singleton:

      public class ServicioGestionSolicitudes { private static ServicioGestionSolicitudes instancia = new ServicioGestionSolicitudes(); private ServicioGestionSolicitudes() {} public static ServicioGestionSolicitudes getInstance() { return instancia; } }

      Vamos a crear a siguiente plantilla, llamada Singleton en las plantillas de Eclipse:

      private static final ${enclosing_type} instancia = new ${enclosing_type}(); private ${enclosing_type}() {} public static ${enclosing_type} getInstancia() { return instancia; }

      Ahora vamos a hacer la siguiente prueba. Vamos a crear una nueva clase, y vamos a escribir el siguiente código:

      public class ServicioGestionSolicitudes { Singleton }

      Nos colocamos tras la palabra Singleton, y pulsamos Ctrl+Espacio. Como resultado obtendremos lo siguiente:

      public class ServicioGestionSolicitudes { private final static ServicioGestionSolicitudes instancia = new ServicioGestionSolicitudes(); public ServicioGestionSolicitudes() { } public ServicioGestionSolicitudes getInstancia() { return instancia; } }

      Actividad 8. Crea una plantilla llamada factory que cree un método llamado del mismo modo, que devuelve un nuevo objeto de la clase que contiene al método.

      Toma una captura en el momento en que estés creando la plantilla y guárdala con el nombre Act8-eclipse.png.

      Tareas

      Podemos colocar marcadores en nuestro código para volver más tarde para hacer algún cambio. Son comentarios, pero también son algo más que comentarios, ya que la "Vista de Tareas" hace un seguimiento de ellos.

      Inicialmente existen tres marcadores

      • // TODO hace referencia a cosas que quedan por hacer.
      • // FIXME hace referencia a algo que hay que arreglar.
      • // XXX no es nada en concreto. Una simple llamada de atención.

      Si tenemos a lo largo de nuestro código fuente alguna línea del tipo // TODO añadir un adaptador para la lista, podremos localizarlo fácilmente si abrimos la vista de tareas en la opción Ventana\Mostrar vista\Tareas.

      Podemos utilizar estos marcadores, o bien crear nuestros propios marcadores. Se puede observar que en la imagen aparece un marcador llamado TEMPORAL. Para crear nuestros marcadores, debemos ir a Ventana\Preferencias\Java\Compilador\Códigos de tareas y definir allí nuestras propias etiquetas.

      Actividad 9. Crea un nuevo marcador, llamado IMPORTANTE, de prioridad alta. Después utiliza dicho marcador en el proyecto net.ametsis.ed.Scrum añadiendo algún comentario.

      Toma una captura, llamada Act9-eclipse.png donde se pueda ver en el editor el marcador, y donde también se pueda ver en la "vista de Tareas" con el comentario destacado.

      Actividad 10. Se pide una aplicación en modo consola que nos servirá para gestionar nuestra tienda. En la tienda se vende música, que podemos tener en varios formatos (CD, vinilo y mp3). Podremos asignar nuestra venta a un determinado cliente, con lo que pedimos un sistema para:

      • eliminar/añadir/listar clientes
      • eliminar/añadir/listar música
      • vender música a un determinado cliente

      Las entidades (clientes y música) serán almacenadas en memoria, así que cuando termine la ejecución de la aplicación, desaparecerán (es el inconveniente de no trabajar con una base de datos).

      Como toda aplicación de consola, se manejará usando parámetros y su uso es así:

      • Una vez iniciada la aplicación esta nos mostrará el siguiente mensaje:
      • "Esperando instrucción:"

      Las instrucciones posibles son:

      crear "cliente"|"musica"|"venta"

      • En este caso estamos creando una entidad, por ejemplo, crear cliente. El comando crear pedirá al usuario que se rellenen los campos de cada entidad y al final mostrará un mensaje del estilo Cliente creado correctamente con código X, donde el código es un número incremental independiente para cada entidad.
      • Cuando creemos una venta al tener que asignarla a un cliente nos pedirá el código de dicho cliente y para insertar música en esa venta también se hará mediante código (solo se podrá asignar una música a una venta); siempre que suceda esto se notificará al usuario de la siguiente manera:
      • Música X asignada a la venta actual

      listar "cliente"|"musica"|"venta"

      • Listará los elementos actuales almacenados en memoria.

      eliminar "cliente"|"musica"|"venta [codigo]"

      • Eliminará el elemento que se pase por código. El código se podrá pasar como parámetro de la instrucción o no; si no se pasa, se deberá preguntar al usuario por él. Un ejemplo pasando el código sería eliminar cliente 1.

      cerrar

      • Cuando la aplicación reciba esta instrucción, finalizará su ejecución.

      Desarrolla la aplicación en equipos de 3. Deberéis de utilizar la metodología Scrum para planificar las tareas. Una vez esté finalizada, entrega el proyecto en un archivo .zip. Para superar con la máxima nota, se probarán los comandos del ejercicio, cuyo funcionamiento debe ajustarse a lo pedido en el enunciado. En caso contrario, la nota máxima será 7 y dependerá del código fuente. En el código fuente se valorarán aspectos como: corrección del código, elegancia del código fuente, arquitectura modular con el uso de diferentes paquetes, clases, etc.