No es un bug, es una característica no documentada

Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas

12/6/15

Programación. Manual completo del módulo

0:57 Posted by Inazio , , , No comments
Por fin puedo decirlo. Ha sido un año trabajando duro, pero he conseguido aprobar el módulo (bueno, el curso completo, que coño) de programación, y lo que es aún más importante, aprender y no poco.

Durante todo el curso he estado realizando un manual exclusivamente de este módulo, tanto teoríco como práctico. Si quieres descargartelo puedes visitar la sección de descargas de este blog (pulsa aquí o navega por el menú habilitado para ello).

Cualquier duda, mejora, consulta, corrección, etc. agradecería un comentario en la propia entrada de la descarga.

Espero que le podaís sacar partido. Saludos


5/5/15

Programación. GUIs en Java

0:25 Posted by Inazio , No comments
La API de Java tiene dos librerías para desarrollar aplicaciones con interfaz gráfica.
·         AWT (Abstract Window Toolkit). Forma parte de la JFC (Java Foundation Classes – Clases base de Java). Los componentes se encuentran en la librería Java.AWT
·         SWING. Componentes programados con código no nativo, lo cual los hace más portables. Son componentes más potentes, y se identifican con una J antes del nombre del componente. Los componentes se encuentran en la librería javax.swing y son todo subclases de la clase JComponent.Swing. Forma parte de Java2.

import javax.swing.*;
public class PruebaGUI{
   public static void main(String [] args){
     JFrame frame = new JFrame(“Ventana Hola mundo”);
     frame setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
     JLabel label = new JLabel(“Hola mundo”);
     frame.add(label);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
   }
}

Veamos el código anterior línea por línea:

JFrame frame = new JFrame(“Ventana Hola mundo”);

Toda aplicación Java que utilice una interfaz con Swing necesita como mínimo un contenedor de alto nivel, que puede ser alguno de los siguientes:
·         JFrame. Implementa una ventana. Las ventanas principales de una aplicación deberían de ser un JFrame.
·         JDialog. Implementa una ventana de tipo diálogo. Este tipo de ventanas se utilizan como ventanas secundarias y generalmente son llamadas por ventanas padre del tipo JFrame.
·         JApplet. Un applet es una aplicación Java que se ejecuta dentro de un navegador web en la máquina del cliente. Un JApplet es una zona de la ventana del navegador donde se va a ejecutar un applet.

En nuestro ejemplo, el contenedor de alto nivel utilizad es JFrame.

frame setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

Esta línea indica lo que hará la aplicación cuando se pulse el botón cerrar. La operación EXIT_ON_CLOSE hace que el programa termine cuando el usuario cierra la ventana.

En este caso, como el programa tiene un solo frame, el cierre del mismo terminará el programa.

JLabel label = new JLabel(“Hola mundo”);
frame.add(label);

Una vez creada la ventana o frame, el siguiente paso es añadir componentes en él. En estas dos líneas se crea un componente de tipo Label y lo añadimos al frame.

frame.pack();

El método pack lo que hace es establecer el tamaño del frame.

En este caso lo que estamos haciendo es darle el tamaño más adecuado a todos los componentes.

Si quisiésemos establecer explícitamente el tamaño de la ventana, podríamos utilizar los métodos setSize y setBounds (con éste método también se puede establecer la posición de la ventana).

frame.setLocationRelativeTo(null);

Esta línea es para centrar la aplicación en pantalla. Es decir, centramos la ventana creada.

frame.setLocationRelativeTo(null);

Como último paso, necesitamos hacer visible la ventana. Otra forma sería utilizando el método show.

Componentes Swing

Los componentes Swing son clases.
Su utilización no difiere a la utilización de cualquier otro objeto.

Swing provee objetos para todos los componentes básicos que se ejecutan en interfaces gráficas.

Componente Swing más comunes


·         JButton. Botón estándar
·         JLabel. Etiqueta de texto estándar
·         JTextField. Cuadro de texto
·         JTextArea. Cuadro de texto multilínea
·         JCheckBox. Casilla de verificación
·         JRadioButton. Botón de opción
·         JComboBox. Lista desplegable
·         JScrollBar. Barra de desplazamiento

Más información y componentes: Consultar la API de Java

Contenedores Swing

Como ya hemos visto, para desarrollar una aplicación en Java necesitamos utilizar un contenedor de nivel superior, como puede ser un JFrame, un JDialog o un JApplet (este último en el caso de que queramos que la aplicación se ejecute dentro de un navegador web).

Existen otro tipo de contenedores intermedios, como son los JPanel. Cuando hay que colocar una serie de componentes en una ventana es imprescindible utilizar paneles para darle a la interfaz la disposición y el aspecto deseado.

JPanel

Los paneles son contenedores de componentes ligeros.
Pueden a su vez contener otros paneles.
Pueden tener un color de fondo (incluso ser opacos o transparentes).
Pueden cambiar la apariencia de los bordes.
Las posibilidades de JPanel son mínimas. Se pueden hacer pocas operaciones con los paneles aparte de establecer bordes o formas de organización de los componentes en ellos (con el método setLayout).

Organización de los controles en un contenedor


Para organizar los controles en un panel es necesario establecer en él un Layout Manager o Administrador de Diseño.

Un contenedor tiene establecido por defecto un Layout Manager pero es posible (e incluso deseable) cambiarlo cuando se realiza una aplicación.

El Layout Manager por defecto es FlowLayout, que coloca los componentes en el contenedor de izquierda a derecha.

Otros posibles Layout Managers son:
·         BorderLayout. Divide el contenedor en cinco partes (norte, sur, este, oeste y centro)
·         CardLayout. Permite colocar grupos de componentes diferentes en momentos diferentes de la ejecución de programa.
·         GridLayout. Coloca los componentes en filas y columnas.
·         GridBagLayout. Coloca los componentes en filas y columnas, pero un componente puede ocupar más de una fila.
·         BoxLayout. Coloca los componentes en una fila o columna ajustándose.

Uso de Layout Manager


Añadir esta línea al comienzo:

import java.awt.*;

Los Layout Manager se pueden establecer al crear el objeto

JPanel panel = new JPanel(new FlowLayout());

O bien una vez que el objeto ha sido creado con el método setLayout

JPanel panel = new JPanel();
Panel.setLayout(new FlowLayout());

En ocasiones, cuando se añade un componente al contenedor hay que especificar la posición que queremos que ocupe en el mismo. El como hacerlo depende del Layout Manager elegido.

Ejemplo FlowLayout

import java.awt.*; // Necesario para los layout
import javax.swing.*;
public class PruebaGUI{
   public static void main(String[] args){
     JFrame frame = new JFrame(“Ventana”);
     JPanel panel = new JPanel(new FlowLayout());      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
     JLabel label1 = new JLabel(“Etiqueta 1”);
     JLabel label2 = new JLabel(“Etiqueta 2”);
     JLabel label3 = new JLabel(“Etiqueta 3”);
     panel.add(label1);
     panel.add(label2);
     panel.add(label3);
     frame.add(panel);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
   }
}

Ejemplo BorderLayout

import java.awt.*; // Necesario para los layout
import javax.swing.*;
public class PruebaGUI{
   public static void main(String[] args){
     JFrame frame = new JFrame(“Ventana”);
     JPanel panel = new JPanel(new BorderLayout());    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
     JLabel label1 = new JLabel(“Etiqueta 1”);
     JLabel label2 = new JLabel(“Etiqueta 2”);
     JLabel label3 = new JLabel(“Etiqueta 3”);
     panel.add(“South”, label1);
     panel.add(“North”, label2);
     panel.add(“East”, label3);
     frame.add(panel);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
   }
}

Organización de los elementos en una ventana

Para organizar los elementos en una ventana también se puede utilizar el Layout Manager que queramos.

Podemos organizar los elementos en paneles y los paneles en la ventana, por ejemplo.

Ejemplo.

import.java.awt.*;
import javax.swing.*;
public class PruebaGUI{
   public static void main(String[] args){
     JFrame frame = new JFrame(“Ventana”);
     JPanel panel = new JPanel(new GridLayout());
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
     JLabel label1 = new JLabel(“Etiqueta 1”);
     JLabel label2 = new JLabel(“Etiqueta 2”);
     JLabel label3 = new JLabel(“Etiqueta 3”);
     JLabel label4 = new JLabel(“Etiqueta 4”);
     panel.add(“South”, label1);
     panel.add(“North”, label2);
     panel.add(“East”, label3);
     frame.add(panel);
     frame.add(label4);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
   }
}

Apariencia de las ventanas

La apariencia (look and feel) genérica de las ventanas es algo que puede ser modificado según diferentes estilos.

Java tiene un gestor de la interfaz de usuario UIManager que controla la apariencia genérica de las ventanas y de los comportamientos que tienen.

La clase que controla todo esto es javax.swing.UIManager.

Para recuperar el estiulo del sistema utilizar

UIManager.setSystemLookAndFeelClassName();

Dependiendo del sistema que tengamos devolverá una apariencia (predefinida para ese sistema) u otra:

·         Multiplataforma. “javax.swing.plaf.meta.MetalLookAndFeel”
·         Windows. “com.sun.java.swing.plaf.windows.WindowsLookAndFeel”
·         Solaris. “com.sun.java.swing.plaf.motif.MotifLookAndFeel”
·         Mac. “javax.swing.plaf.mac.MacLookAndFeel”
·         Unix / Linux. “com.sun.java.swing.plaf.gtk.GTKLookAndFeel”

Diferentes apariencias

En un sistema Windows, no pongo nada, establezco que el look and feel sea el Multiplataforma(2) (o el Metal(3), lo mismo llamado de otra manera).

Establezco el look and feel por defecto (1). En este caso como estoy bajo Windows se aplica la apariencia Windows.

El Motif(4). Apariencia Solaris. Utilizable en cualquier plataforma.

Si intento apariencia Mac o Linx al estar bajo Windows, ERROR.

¿Cómo lo hago?

1.      Establezco el look and feel por defecto.

try{
   UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClaseName());
}
catch(Exception e){
   e.printStackTrace();
}

2.      El multiplataforma

try{
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClaseName());
}
catch(Exception e){
   e.printStackTrace();
}


3.      El Metal

try{
   UIManager.setLookAndFeel(“javax.swing.plaf.metal.MetalLookAndFeel”);
}
catch(Exception e){
   e.printStackTrace();
}


4.      El Motif

try{
UIManager.setLookAndFeel(“com.sun.java.swing.plaf.motif.MotifLookAndFeel”);
}
catch(Exception e){
   e.printStackTrace();
}

Eventos

En todas las aplicaciones, cuando el usuario interactúa con las mismas, suceden cosas (pulsa un botón, cierra una ventana, mueve una barra de desplazamiento, etc.).

Ante estas situaciones, el programa deberá realizar algunas acciones, que lógicamente deberán estar programadas.

Existen una serie de manejadores / controladores de eventos (o listeners) los cuales deberán de ser asociados al componente para que éste ejecute la respuesta necesaria.

Los listeners están especializados dependiendo del evento ocurrido, y sólo tienen sentido para componentes determinados

Listeners asociados a componentes


A continuación tratamos los listeners más comunes, siguiendo el siguiente esquema:

Listener (componentes a los que es aplicable): Acción a la que corresponden

·         ActionListener(JButton, JTextField, JComboBox…): Presinoar el botón, pulsar Intro, elegir una opción
·         AdjustmentListener(JScrollBar…): Mover la barra de desplazamiento…
·         FocusListener(JButton, JTextField, JComboBox, …):  Las acciones de este listener son obtener y perder el foco (colocarnos en el componente e irnos del mismo cuando esté activo).
·         ItemListener(JCheckBox, …): Seleccionar y deseleccionar la opción
·         KeyListener(JTextField, JTextArea, …): Pulsar una tecla cuando el componente tiene el foco
·         MouseListener(Multiples componentes): Acciones como pulsar el botón del ratón
·         MouseMotionListener(Multiples componentes): Acciones como arrastrar o pasar por encima del objeto
·         WindowListener(JFrame): Acciones relativas a la ventana, como por ejemplo, cerrarla.

Para más información sobre Listeners, consultar la API de Java, concretamente el paquete java.awt.event (que casualmente es el que tenemos que importar cuando queremos utilizar eventos).

Ejemplo 1 de Listener

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class PruebaGUI1{
   private static JLabel label = new JLabel(“--”);
   private static JButton btnlimpia = new JButton(“Limpia”);
   private static JButton btnescribe = new JButton(“Escribe”);

   public static void acciones(ActionEvent e){
     Object obj = e.getSource(); // Permite saber que objeto generó el evento
     if (obj == btnlimpia)
         label.setText(“”);
     if (obj == btnescribe)
         label.setText(“Hola mundo”);
   }
}

La función escribe una cosa u otra en la etiqueta de la aplicación en función del botón que generó el evento.

public class principal{
   public static void main(String[] args){
     JFrame frame = new JFrame(“Controlador de eventos”);
    
     btnlimpia.addActionListener(new ActionListener(){
         public void actionPerformed(ActionEvent e){
              acciones(e);
         }
     });

     btnescribe.addActionListener(new ActionListener(){
         public void actionPerformed(ActionEvent e){
              acciones(e);
         }
     });

     frame.add(label);
     frame.add(btnlimpia);
     frame.add(btnescribe);

     frame.addWindowListener(new WindowsAdaper(){
         public void windowClosing(WindowEvent e){
              System.exit(0);
         }
     }

     frame.setLayout(new GridLayout(0,1));
     frame.pack();
     frame.setVisible(true);
   }
}

Se crea un listener para cada botón de la aplicación.

Los dos ejecutarán el método acciones que se ocupará de distinguir cuál ha generado el evento y hacer lo que corresponde.

El listener ActionListener tiene como único método a implementar actionPerformed (que es obligatorio implementar, al ser ActionListener una interface).

El listener WindowAdaptar, en cambio, es una clase.

Crear aplicaciones independientes

¿Necesito Eclipse / NetBeans para ejecutar aplicaciones? NO

Puedo exportar mis aplicaciones (gráficas) a ficheros .jar ejecutables directamente con un simple doble clic.

Para ello, en Eclipse, File à Export, indicar que quiero exportar como “Runnable jar file”, indicar lo que quiero exportar a incluir en el fichero jar (clases y librerías), el nombre del fichero a crear y se generará.


Ese fichero .jar es directamente ejecutable bajo cualquier sistema operativo que disponga de máquina virtual de Java.

19/4/15

Programación. Desarrollo de software orientado a objetos (II)

20:49 Posted by Inazio , No comments

Ficheros en Java


Manejar ficheros en Java es muy sencillo. En el paquete java.io encontramos las herramientas que necesitamos.

1.      La clase File representa un fichero o directorio (que no tiene por qué existir todavía). File nos permite averiguar mucha información útil (ruta del fichero, si se puede escribir, etc).
2.      Por otro lado, si quieremos tratar con ficheros de texto usaremos streams basados en caracteres, para el resto de casos utilizaremos streams basados en bytes.

Para el uso de la clase File ver antes sección anterior.

El uso de los streams de ficheros dependerá de lo que queramos hacer…

Para la lectura o escritura en ficheros de texto usamos FileReader y FileWriter. Ejemplo:

FileReader fichLec = new FileReader(cadNombre1);
FileWriter fichEsc = new FileWriter(cadNombre2);
char caracter = fichLec.read();
fichEsc.write(carácter);

Para lectura o escritura de datos en ficheros usamos DataInputStream y DataOutputStream. Ejemplo:

DataInputStream fichLec = new DataInputStream(cad1);
DataOutoutStream fichEsc = new DataOutputStream(cad1);
int codArticulo = fichLec.readInt();
int numUnidades = fichLec.readInt();
double precio = fichLec.readDouble();
fichEsc.writeInt(codArticulo);
fichEsc.writeInt(numUnidades);
fichEsc.writeDouble(precio);

Todos los constructores de streams de ficheros admiten como parámetro de entrada un objeto de la clase File.

Se aconseja que el acceso a los ficheros se haga mediante un buffer de memoria para mejorar la eficiencia. Para ello debemos usar un stream con capacidad de almacenamiento.

Ejemplo:

File fichero = new File(“pedidos.dat”);
DataOutputStream dos = new DataOutputStream(fichero);
BufferedOutputStream stream = new BufferedOutputStream(dos);
stream.writeInt(codArticulo);
stream.writeDouble(precio);

Presistencia de objetos

Hasta ahora todos los objetos que hemos creado se han alojado en la memoria del ordenador. Ahora queremos almacenarlos en el disco duro, de manera que pueda recuperarlos cuando reinicie el ordenador.

La persistencia de objetos se ocupa de escribirlos y leerlos desde el disco duro.

Normalmente los objetos en:
è Ficheros binarios
è Bases de datos

Persistencia de objetos en ficheros

Partimos del siguiente ejemplo:

Persona p = new Persona(“Ana”, “Lopez Haro”);
Mascota m = new Mascota(“Tobi”);
Coche c = new Coche(“4324-FJT”);
p.compraMascota(m);
p.compraCoche(c);



Si ahora quieres escribir el objeto Persona al disco duro, se escribiría lo siguiente


A la operación de convertir un objeto en una hiera de bytes se le llama serialización.

Lo que ocurre después es que primero se escribe el árbol de objetos referenciados por el objeto y después el objeto propio.


Cuando se realiza la lectura del fichero, se hace la operación inversa y el objeto se reconstruye en memoria con todas sus referencias apuntado a los objetos adecuados.

Implementación en Java

Para que un objeto pueda ser escrito y leído a un fichoer debe implementar la interfaz Serializable. Si el objeto posee referencias a otros objetos, éstos también deben implementar la interfaz Serializable. En nuestro ejemplo:


La interfaz Serializable no contiene ningún método. Actúa como un marcador del objeto, indicando que éste puede ser serializado.

Por otro lado, necesiamos un stream de entrada y otro de salida capaces de trabajar con objetos:
è java.io.ObjectInputStream
è java.io.ObjectOutputStream

Ahora tenemos que hacer:
1.      Crear un objeto del stream que conecta con el fichero
2.      Conectar dicho stream con el stream de objetos
3.      Llamar al método que nos haga falta
a.       + void writeObject(Object obj) para escribir un objeto en disco
b.      + Object readObject() para leerlo del fichero
4.      Capturar las excepciones que puedan aparecer
5.      Cerrar los streams

import java.io.Serializable;
public class Coche implements Serializable{
   private String matricula;
   private double maxLitros;
}

public class Mascota implements Serializable{
   ...
}

public class PruebaSerializable{
   public static void main(String[] args){
     FileOutputStream fichSalida = null;
     ObjectOutputStream oos = null;
     FileinputStream fichEntrada = null;
     ObjectInputStream ois = null;

     try{
         fichSalida = new FileOutputStream(“c:/persona.obi”);
         oos = new ObjectOutputStream(fichSalida);
         oos.writeObject(p);
     }
     catch(IOException e){
         System.out.println(“Se produjo un error de apertura”);
     }
     finally{
         try{
              if(oos != null)
                   oos.close();
              if(fichSalida != null)
                   fichSalida.close();
         }
catch(IOException e){
     System.out.println(“No se consiguió cerrar el fichero correctamente”);
}

     }

     try{
         fichEntrada = new FileInputStream(“c:\persona.obi”);
         ois = new ObjectInputStream(fichEntrada);
         p = (Persona)ois.readObject();
     }
     catch(ClassNotFoundException e){
         System.out.println(“Contenido de fichero no esperado”);
     }
     finally{
         try{
              if (ois != null)
                   ois.close();
              if (fichEntrada != null)
                   fichEntrada.close();
         }
         catch(IOException e){
              System.out.println(“No se consiguió cerrar el fichero correctamente”);
         }
     }
     System.out.println(“Se leyó el fichero correctamente”);
   }
}

Persistencia de objetos en bases de datos

Según su relación con el enfoque orientado a objetos podemos clasificar las bases de datos en tres grupos:

è Relacionales puras
·         Tecnología madura y eficiente
·         Problema: Se basan en un modelo plano de datos. Los objetos pueden contener a su vez otros objetos y éstos, otros a su vez.
·         Solución: Bastante costosa en tiempo y esfuerzo. Hay que establecer una correspondencia entre objetos y tablas haciendo un buen trabajo de diseño
è Orientadas a objetos puras
·         Se soluciona el problema anterior. Ahora la correspondencia entre objeto de programación y el objeto guardado en disco es directa, creándose un grafo de objetos interrelacionados y navegables.
·         Tecnología lenta, ineficiente y poco flexible a la hora de hacer consultas (se pierde la flexibilidad del SQL).
è Objeto-Relacionales o mixtas:
·         Combina las ventajas de las dos anteriores
·         Son BBDD relacionales, así se conserva la velocidad y la eficiencia. Sin embargo incorporan nuevos tipos de datos que permiten modelar los objetos a elementos de una base de datos relacionales.
·         A partir de la versión 8i de Oracle se incorporan los nuevos tipos abstractos de datos que pueden relacionarse mediante herencia y poseen métodos que pueden ser implementados en Java o SQL.

El framework Collection


Tablas, listas, pilas, colas, árboles, conjuntos, diccionarios…

Las colecciones o contenedores de objetos son estructuras de datos muy recurridas y necesarias. Su utilización implica normalmente:
è La adaptación de la colección al tipo de elemento que queremos almacenar
è La escritura de los algoritmos que nos hagan falta: ordenación, búsqueda, recorrido…

Estas tareas son repetitivas, tediosas y propensas a errores.

Java soluciona el problema utilizando las siguientes herramientas:
è Tipos genéricos. Permite escribir el código de una colección sin especificar el tipo del objeto que almacena.
è FrameWork Collection. Es una arquitectura unificada para la representación y manipulación de colecciones de objetos.

Tipos génericos


Vamos a crear un contenedor muy simple capaz de guardar un objeto de una clase cualquiera:

public class Caja{
   private Object object;
   public void set(Object object){
     this.object = object;
   }
   public Object get(){
     return object;
   }
}

Si quisiera usar la caja para almacenar SÓLO objetos de la clase Bombilla podríamos hacer:

public class PruebaCajaBombillas{
   public static void main(String args[]){
     Caja cajaBombillas = new Caja();
     // Creamos una bombilla y la guardamos
     cajaBombillas.set(new Bombilla());
     // Al recuperarla hay que hacer un casting
     Bombilla b = (Bombilla) cajaBombillas.get();
     // Nada nos impide guardar una cadena
     cajaBombillas.set(new String(“hola”));
     // Que provocaría una ClassCastException al hacer
     b = (Bombilla)cajaBombillas.get();
   }
}

Esta forma de usar la caja nos plantea los siguientes problemas:
·         Tenemos que acordarnos de hacer el casting al recuperar el objeto guardado
·         Podemos guardar otro tipo de objeto por equivocación y el compilador no se da cuenta. Esto provoca que se lance una excepción en tiempo de ejecución.

Ahora vamos a rescribir nuestra clase Caja usando tipos genéricos y se resuelven problemas de un golpe:

public class Caja<Tipo>{
   private Tipo objeto;
   public void set(Tipo objeto){
     this.objeto = objeto;
   }
   public Tipo get(){
     return objeto;
   }
}

Indicamos que en la caja va a aparecer un tipo de datos que todavía no se ha concretado, y después lo usamos como si se conociera.

public class PruebaCajaBombillas{
   public static void main(String args[]){
     // Al declarar la referencia y crear el objeto de la clase cala concreatamos el tipo genérico
     Caja<Bombilla> cajaBombillas = new Caja<Bombilla>();
     cajaBombillas.set(new Bombilla());
     // Ya no tenemos que hacer el casting
     Bombilla b = cajaBombillas.get();
     // El compilador identifica el error
     cajaBombillas.set(new String(“Hola”));
   }
}

Más sobre los tipos genéricos

Se puede usar el tipo genérico como parámetro al crear constructores. Ejemplo:

public class Caja(Tipo o){
   this.objeto = o;
}

Además podemos usar más de un tipo genérico en una clase. Ejemplo:

public class Armario<Tipo1, Tipo2>{
   Tipo1 cajonSuperior;
   Tipo2 cajonInferior;
   …
}

Podemos limitar las clases que pueden concretar al tipo genérico. Ejemplo:

public class Jaula<Tipo extends Animal>{
   // Admite enjaular cualquier objeto Animal sea de la clase que sea: Tigre, Mosquito, Ballena. Ya no podríamos enjaular un objeto String
}

Se puede usar con interfaces

public interface Contenedor<Tipo>{
   public void introducir(Tipo cosa);
   public void extraer();
   public Tipo consultarConenido();
}

Se pueden establecer relaciones de herencia entre clases o interfaces genéricas, formando jerarquías.

El FrameWork Collection


Lo forman un conjunto de clases e interfaces ubicadas mayoritariamente en el paquete java.util y que se estructuran de la siguiente manera:

·         Las interfaces definen las operaciones de cada colección de elementos
·         Las clases pueden ser de dos tipos
o   Las que implementan las interfaces anteriores
o   Las que implementan los algoritmos “clásicos” de las colecciones: ordenar, buscar un elemento, buscar máximo…


Veamos que hace cada tipo de interfaz:

·         set(conjunto). Es una colección de elementos en la que no existe un orden y que no admite duplicados
·         list(lista). Es una colección de elementos ordenada y en la que se admiten duplicados
·         queue(cola). Es una colección de elementos ordenada con comportamiento FIFO (First In, First Out).
·         map(mapa o diccionario). Es una colección de pares de objetos Clave – Valor que funciona como un diccionario en el que se busca una palabra (clave) para encontrar su definición (Valor).

Las claves no pueden repetirse pero sí los valores. Ejemplo: ‘súbito’ y ‘repentino’ podrían tener la misma definición.
·         sortedSet y sortedMap. Además, almacena sus elementos según un criterio de orden.

Las implementaciones de las interfaces se basan en distintas estructuras de datos.

·         ArrayList. Tabla capaz de redimensionarse
·         TreeMap y TreeSet. Árbol
·         LinkedList. Lista enlazada
·         HashMap y Haztree. Tabla de dispersión (o tabla Hash).
·         LinkedHashMap y LinkedHashSet. Tabla de dispersión combinada con una lista enlazada que mantiene el orden en el que se insertaron los pares Clave – Valor.

Programa de ejemplo. PruebaHashMap.java

import java.util.HashMap;
import java.io.*;

public class PruebaHashMap{
   public static void main(String [] args) throws IOException{
     HashMap<String, String> diccionario = new HashMap<String, String>();
     String palabra, respuesta;
     Teclado t = new Teclado();

     diccionario.put(“Hola”, “Hello”);
     System.out.println(“Mini diccionario español - inglés”);
     do{
         palabra = t.leerString(“dime una palabra”);
         if (diccionario.containKey(palabra))
              System.out.println(palabra + “ se dice en inglés ” + diccionario.get(palabra));
         else
              System.out.println(palabra + “ no se encuentra en el diccionario”);
         respuesta = t.leerString(“Quieres buscar otra palabra(S/N):”);
     }while (respuesta.equalsIgnoreCase(“S”); // equalsIgnoreCase lee texto no case sensitive
   }
}

Recorrido de colecciones de elementos


Ahora queremos recorrer una colección (de cualquier tipo) desde el primer hasta el último elemento.

Para ello Java aplica el patrón de diseño iterador que se basa en las siguienter interfaces:

·         La interfaz Iterable posee sólo un método que devuelve un objeto que implementar la interfaz Iterator
·         La interfaz Iterator posee los métodos hasNext y next que nos permite recorrer una colección.


import java.util.*;

public class PruebaIteradores{
   public static void main(String[] args){
     int i;
     Interator iter;

     LinkedList<String> lista = new LinkedList<String>();
     HashSet<String> conjunto = new HashSet<String>();
     TreeSet<String> arbol = new TreeSet<String>();

     for (i = 1; i <= 10; i++){
         lista.add(“Elemento ” + i + “ de la lista”);
         conjunto.add(“Elemento ” + i + “ de la lista”);
         arbol.add(“Elemento ” + i + “ de la lista”);
     }

     iter = lista.iterator();
     while(iter.hasNext())
         System.out.println(inter.next);

     iter = conjunto.iterator();
     while(iter.hasNext())
         System.out.println(inter.next);

     iter = arbol.iterator();
     while(iter.hasNext())
         System.out.println(inter.next);

   }
}

Las clases que implementan los algoritmos “clásicos” de las colecciones son:
è Collections. Capaz de operar con objetos que implementan la interfaz Collection.
è Arrays. Capaz de trabajar con objetos de tipo tabla

Podemos encontrar algoritmos para ordenar, desordenar, buscar el máximo o mínimo, invertir el orden de los elementos, rellenar todo los elementos con un valor, convertir de Collection a tabla y viceversa…

Estos algoritmos se presentan como métodos estáticos y que usan tipos genéricos.

Nos fijamos en una de las versiones del método de ordenación de la clase Collections:

public static void sort(List<T> lista);

La documentación de Java dice que ordena una lista según el orden natural de los elementos… ¿y qué orden es ese?

Para establecer el orden natural de los objetos de una clase Java obliga a implementar la interfaz Comparable.

Si hacemos

objeto1.compareTo(objeto2)

Se debe obtener
·         -1 si objeto1 < objeto2
·         0 si objeto1 = objeto2
·         1 si objeto1 > objeto2

Programa de ejemplo:

public class Persona implements Comparable{
   // Propiedades
   private String nombre;
   private String apellido;
   private String dni;
  
   // Métodos de comparación
   public int compareTo(Object o){
     String nomCompleto = apellido + “ ” + nombre;
     Persona p = Persona(o);
     return nombreCompleto.compareToIgnoreCase.p.getApellido() + “ ” + p.getNombre();
   }
}

import java.util.*;
public class PruebaOrdenacion{
   public static void main(String[] args){
     Iterator iter;
     LinkedList<Persona> lista = new LinkedList<Persona>();

     lista.add(new Persona(“Juan”, “Lopez Torres”);
     lista.add(new Persona(“Ana”, “Lopez Torres”);
     lista.add(new Persona(“Paula”, “Zambrano Sanchez”);
     lista.add(new Persona(“Ambrosio”, “Aguilar Ramos”);
     lista.add(new Persona(“Maria”, “Lopez Torres”);

     iter = lista.iterator();
     while (iter.hasNext())
         System.out.println(iter.next());
    
     System.out.println(“Y ahora ordenados…”);
     Collections.sort(lista);

     iter = lista.iterator();
     while (iter.hasNext())
         System.out.println(iter.next());
   }
}

La clase Arrays se comporta de la misma forma que Collections pero para objeto de tipo tablas (arrays).