Professional Documents
Culture Documents
Programación 1 y 2
Apuntes de la asignatura
Curso 2014-2015
Contenido
1 Introducción 8
1.1 ¿Qué es un ordenador? 8
1.2 Lenguajes de programación 17
1.3 Sistema operativo 21
4 Introducción a la algorítmica 90
4.1 Ejemplos 93
4.2 Algoritmos y coste computacional 115
6 Excepciones 155
6.1 Escribir nuevas excepciones 157
Contenido detallado
1 Introducción 8
1.1 ¿Qué es un ordenador? 8
1.1.1 Hardware del ordenador 9
1.1.2 Ejecución de programas 12
1.2 Lenguajes de programación 17
1.2.1 Ejecución de un programa fuente 20
1.3 Sistema operativo 21
1.3.1 Ficheros 22
1.3.2 Codificación de la información 23
2 Introducción al lenguaje de programación Java 25
2.1 El primer programa 25
2.1.1 Programación en la consola 26
2.1.2 Programación en una plataforma de desarrollo (netbeans) 27
2.2 Fundamentos 29
2.2.1 Palabras reservadas 30
2.2.2 Identificadores 31
2.2.3 Comentarios 34
2.2.4 Clases y objetos. (Tipos y variables) 35
2.2.4.1 Conceptos vinculados a las variables y objetos 37
4
2.4.1 java.lang.String 66
2.4.2 Envoltorios de tipos básicos 68
2.4.3 java.lang.Math 69
2.4.4 java.lang.Object 70
2.4.5 java.lang.System 70
2.4.6 java.util.Arrays 70
2.4.7 java.util.ArrayList 71
2.5 Excepciones 72
4 Introducción a la algorítmica 90
4.1 Ejemplos 93
4.1.1 Máximo común divisor (mcd) 93
4.1.2 sin(x) 96
6
4.1.3 Ordenar 99
4.1.3.1 Algoritmo de selección 100
4.1.3.2 Algoritmo de la burbuja 106
4.1.3.3 Algoritmo de inserción 110
4.2 Algoritmos y coste computacional 115
4.2.1 Algoritmo quicksort 119
6 Excepciones 155
6.1 Escribir nuevas excepciones 157
1
Introducción
1.1
¿Qué es un ordenador?
• Procesa información
• Ejemplos
Programación G-ISTSI 9
• Programable
Por eso:
– Hardware: CPU + RAM (procesador + memoria)
– Software: datos+instrucciones.
– Lenguaje de programación: variables + algoritmos
1.1.1
Hardware del ordenador
• CPU (central process unit) RAM (random access memory)
Programación G-ISTSI 10
virtual. Al hacer esto, el CPU reserva ama espacio en el disco duro para simular memoria escritorio para tener un acceso
El p
JANUARY
distintos lugares cada vez que los necesita. 4 / puede distribuir los documentos que va usar y obtener información de éstos
JANUARY
12 JANUARY
Months
JANUARY
1 /bin/ls
Programación G-ISTSI 15
2 ndisasm /bin/ls
Programación G-ISTSI 16
1.2
Lenguajes de programación
Problemas para programar a nivel de código máquina/lenguaje ensamblador:
• instrucciones muy elementales: necesidad de escribir mucho código, incluso para tareas simples
• instrucciones dependientes del procesador: no portable
Programación G-ISTSI 18
Programación G-ISTSI 19
1.2.1
Ejecución de un programa fuente
Programación G-ISTSI 21
1.3
Sistema operativo
• Problema: dificultad de gestión del ordenador (hardware diferente, tareas complejas de bajo
nivel, ...)
1.3.1
Ficheros
• Unidad de información persistente vinculada a un nombre y organizada en un sistema jerárquico
(carpetas o directorios)
• Ilusión del sistema operativo
Programación G-ISTSI 23
1.3.2
Codificación de la información
• Problema: ¿cómo guardar informacón “humana” (texto, imagen, música, video, ...) en un
ordenador (números)?
• Código: tabla unívoca símbolo ↔ número
– texto: ascii (.txt), ms-word (.doc), openoffice (.odg), ...
– documentos: pdf, tiff, ...
– imagen: bmp, jpeg, gif, png, ...
– música: wav, mp3, ...
– vídeo: mpeg, avi, mov, wmv, ...
• Los programas leen los números y muestran los símbolos o toman los símbolos y guardan los
números
Programación G-ISTSI 24
2
Introducción al lenguaje de programación Java
2.1
El primer programa
public class HolaMundo {
} // ()
} // class
Programación G-ISTSI 26
2.1.1
Programación en la consola
Editor de texto
Consola
Programación G-ISTSI 27
2.1.2
Programación en una plataforma de desarrollo (netbeans)
• Crear un proyecto (sólo la primera vez)
2.2
Fundamentos
Programación G-ISTSI 30
2.2.1
Palabras reservadas
siempre en minúscula
Programación G-ISTSI 31
2.2.2
Identificadores
• Nombres elegidos por el programador para identificar a
– objetos (variables)
– constantes simbólicas
– métodos (funciones)
– clases (tipos)
• Reglas
– cualquier combinación sin espacios en blanco de letras (a-z, A-Z) y dígitos (0-9)
– que no empiece por dígito
– ni sea palabra reservada
Programación G-ISTSI 32
• Convenciones
Las letras de los identificadores son en minúscula, excepto:
– la primera letra (depende del tipo de identificador, ver a continuación)
– si un identificador está compuesto por varias palabras unidas, la primera letra de cada
palabra (desde la segunda) es mayúscula
– las constantes: todas las letras en mayúscula
En concreto
3 prefijo
Programación G-ISTSI 33
2.2.3
Comentarios
// esto es un comentario de una linea
/* esto es un comentario
de
varias lineas */
Conviene comentar:
2.2.4
Clases y objetos. (Tipos y variables)
clase conjunto de objetos con similares características (propiedades y com-
portamientos)
objeto individuo con identidad propia única pero con características similares
a otros individuos de su misma clase.
2.2.4.1
Conceptos vinculados a las variables y objetos
Programación G-ISTSI 38
2.2.4.2
Tipos primitivos más usados
char guarda 1 símbolo de la tabla ascii 4.
constantes 5: ’a’ ’b’ ’8’ ’(’ ...
char letra;
letra = ’J’;
4 Unicode en realidad
Programación G-ISTSI 39
2.2.4.3
Creación de objetos
Programación G-ISTSI 41
Es decir,
6 Puede que que al llamar a una función, ésta lo cree –con new()– y lo devuelva.
Programación G-ISTSI 42
2.2.4.4
Representación del diseño de una clase
• método: acción vinculada a un objeto o a una clase. Puede tener datos de entrada, de salida
y puede consultar o modificar los campos.
• constructor: método especial que sólo se ejecuta una vez (por objeto): al crearlo (con new()).
Se encarga de inicializar los campos. Toda clase debe tener uno al menos.
• método no estático: su acción depende del estado de la clase
Se invoca sobre una referencia a objeto:
String nombre = new String("Jordi");
int l = nombre.length();
• método estático: su acción no depende del estado de la clase. (Sólo puede acceder a otros
elementos estáticos).
Se invoca sobre el nombre de una clase:
double z = Math.sin(2.35);
2.2.4.5
Diseño desdel punto de vista del programador
Programación G-ISTSI 45
2.2.5
Interacción con el usuario (funciones de biblioteca)
2.2.5.1
En la consola
Leer y escribir
public class MiPrograma {
public static void main (String[] args ) throws java.io.IOException {
java.io.BufferedReader entrada =
new java.io.BufferedReader ( new java.io.InputStreamReader (System.in));
String linea;
} // ()
} // class
7 Ojo con leer un string después de un número: queda un salto de línea. Ojo con la coma decimal de los números reales :
puede ser 4.5 o 4,5 dependiendo del ordenador.
Programación G-ISTSI 47
2.2.5.2
En modo gráfico
• Leer y escribir
import javax.swing.JOptionPane;
• Petición de confirmación
import javax.swing.JOptionPane;
2.2.5.3
Conversión desde/a texto
(Funciones de biblioteca)
• String → entero
String s = new String("1234");
int i = Integer.parseInt(s);
2.2.6
Constantes simbólicas
Identificador con un valor fijo asociado.
class MiClase {
public static final double PI = 3.141592654;
// uso
MiClase.PI
MiClase.NUMERO_MAX
Programación G-ISTSI 51
2.2.7
Asignación, expresiones y operadores
Asignación
variable = expresión_del_mismo_tipo;
Algorítmicamente:
var ← expresion
Expresión
2.2.7.1
Operadores
aritméticos + - * / %
entero op entero → entero, real op real → real
int a = 7 / 2;
int b = 7 % 2;
double x = 7.0 / 2.0;
incremento/decremento ++ – –
int a=0;
a++;
ternario ( ? : )
int z;
z = ( a<0 ? 0 : a);
Programación G-ISTSI 54
2.2.8
Estructuras de control del flujo de ejecución()
Bloque grupo de sentencias delimitadas por { }
2.2.8.1
if
if ( condicion ) {
bloque // cuando es verdadera
} else {
bloque // cuando es falsa
}
Algorítmicamente:
si condición
si no
2.2.8.2
else if
if ( condicion_1 ) {
...
} else if ( condicion_2 ) {
...
} else if ( condicion_3 ) {
...
} else {
... // si no se ha cumplido ninguna
}
Programación G-ISTSI 57
2.2.8.3
switch
switch ( expresion_entera_o_char ) {
case valor_1:
...
break;
case valor_2:
...
break;
case valor_3:
...
break;
default:
...
break;
}
Programación G-ISTSI 58
2.2.8.4
while
while ( condicion ) {
...
}
Algorítmicamente:
mientras condición
...
Programación G-ISTSI 59
2.2.8.5
do while
do {
...
} while ( condicion );
Programación G-ISTSI 60
2.2.8.6
for
for (inicializacion ; condicion ; incremento) {
...
}
Equivale a:
inicializacion;
while (condicion) {
...
incremento;
}
Programación G-ISTSI 61
Frecuentemente:
for (i=0; i<=n-1 ; i++) {
...
}
Algorítmicamente:
i
0 −−−−−−−−−−−−−→ n − 1
...
Programación G-ISTSI 62
2.3
Arrays
Array objeto que guarda una lista de valores indexados (0 a n − 1)
1. Declaración de la referencia
double[] numeros;
3. Uso
– tamaño
numeros.length
– acceso a casillas
numeros[3] = 2.4;
– recorrido compacto
for (double x : numeros) {
// hacer algo con x
}
Para copiar habría que escribir un bucle que copie o utilizar métodos de biblioteca (java.util.Arrays.copyOf()
Programación G-ISTSI 65
2.3.1
Arrays de objetos
Array de objetos ≡ array de referencias.
1. Declaración de la referencia y creación del 2. Uso
array palabras[2] = new String("jordi");
String[] palabras = new String[7];
Programación G-ISTSI 66
2.4
Clases de biblioteca más usadas
2.4.1
java.lang.String
Programación G-ISTSI 67
int l;
l = nombre.length();
2.4.2
Envoltorios de tipos básicos
• Son objetos (inmutables) que guardan un valor de tipo primitivo. Agrupan métodos de utilidad.
– char → Character
– boolean → Boolean
– int → Integer
– double → Double
Ver paquete java.lang
Programación G-ISTSI 69
2.4.3
java.lang.Math
Funciones 8 y constantes mátematicas. Ver paquete java.lang
x = Math.abs( y );
x = Math.sin( y );
x = Math.cos( y );
x = Math.tan( y );
y = Math.asin( x );
y = Math.acos( x );
y = Math.atan( x );
x = Math.ceil( y );
x = Math.floor( y );
x = Math.exp( y );
x = Math.log( y );
a = Math.min( b, c);
a = Math.max( b, c);
a = Math.pow( b, c);
x = Math.random();
8 Métodos estáticos
Programación G-ISTSI 70
2.4.4
java.lang.Object
Todas las clases de Java derivan de esta: son también de tipo Object y heredan sus métodos.
Ver paquete java.lang
2.4.5
java.lang.System
Clase no instanciable con métodos de utilidad.
2.4.6
java.util.Arrays
Clase no instanciable con métodos de utilidad para la manipulación de arrays: búsquedas, com-
paciones, copias, ordenar, ...
Ver paquete java.util
Programación G-ISTSI 71
2.4.7
java.util.ArrayList
Clase que implementa un array redimensionable de objetos.
ArrayList<String> palabras = new ArrayList<String>();
2.5
Excepciones
• Excepción: situación anómala durante la ejecución de un progama, en muchos casos debida a
una condición del entorno (y no a un fallo de programación)
• Puede ser
– No corregible: el programa termina
– Corregible, el programa termina si no se ha previsto la corrección.
En Java
Si una llamada del código habitual falla, la ejecución salta inmediatamente al catch con
el tipo de excepción disparada por el fallo.
2. Pasar la excepción a quien nos llame: añadiendo throws LaQueSeaException al final del
nombre del método y antes de {
) ... {
En este caso, si hacemos una llamada y ésta falla, nuestro método también falla, rediri-
giendo la excepción a quién nos llamó.
Programación G-ISTSI 74
3
Crear nuevas clases
3.1
Programar
• Un programa es la solución a un problema
• Un problema se enuncia mediante una serie de requerimientos que definen las distintas inter-
acciones que pueden darse entre el usuario y el programa.
Para cada interacción se detalla los datos de entrada y de salida (requerimientos funcionales)
y comportamiento y aspecto (requerimientos no funcionales).
Esto puede modelarse con la ayuda de diagramas de casos de uso de UML (Unified Modelling
Language).
• El siguiente paso es realizar el diseño de la arquitectura del programa. Se trata de decidir
– qué piezas forman el programa: en OO son las clases
– qué características tiene cada clase (qué metodos públicos tiene)
– cómo están relacionadas las clases entre sí (las principales relaciones son hereda de, tiene,
usa).
– cómo interactúan los objetos del programa en cada caso de uso (a qué otros métodos
llama un método dado).
Programación G-ISTSI 75
Para diseñar las clases y sus relaciones se puede utilizar un diagrama de clases de UML 9.
Para detallar la interacción entre los objetos durante la ejecución se puede utilizar diagramas
de comunicación y diagramas de secuencia. Una interacción de estas es la expresión de un
algoritmo global (más general) que afecta a distintos métodos.
• Finalmente hay que completar la parte privada de las clases:
– qué variables y métodos privados tiene
– cuál es el algoritmo de cada método
3.2
El diseño es lo importante
• La parte pública de una clase se llama interfaz.
• El interfaz define cómo se usa la clase. Es decir, qué contructores y qué metodos tiene; y para
cada uno se indica qué valores recibe y devuelve. Por ejemplo:
• Aún antes de implementar la clase, se debe escribir código que utilice sus métodos para hacerse
una idea su usabilidad:
Coche c1;
c1.acelera(90);
int v = c1.dimeVelocidad();
c1.acelera (-10);
3.3
Implementar un diseño
Si hemos diseñado la interfaz de una clase, antes de implementarlo hay que decidir qué hay en la
parte privada. Para el diseño anterior es sencillo.
A continuación habría que decidir los algoritmos apropiados para cada método. En este caso son
triviales y podemos escribir directamente el código 10.
}
} // ()
} // class
• public – private
• perfil de cada método: valores de entrada y valor de retorno
• código de cada método. return
Programación G-ISTSI 81
(El compilador comprueba que la implementación tiene los mismo métodos que el interfaz).
Programación G-ISTSI 82
3.4
Otro ejemplo: la clase Punto
• Diseño de la clase Punto
• Ejemplo de uso:
Punto p1 = new Punto();
Punto p2 = new Punto(3.0, 4.0);
Punto p3 = new Punto(1.2, 3.4);
double r;
r = p3.getY();
r = p1.distancia (p2);
p1 = p2.suma (p3);
Programación G-ISTSI 83
private double x;
private double y;
public Punto () {
x = 0.0;
y = 0.0;
} // ()
3.5
Relación entre el diseño de un método, su perfil, y la forma de llamarlo
Programación G-ISTSI 87
3.6
Reglas de acceso publico/privado
• Se puede acceder a la parte privada sólo desde código escrito dentro de la misma clase.
• Las variables auxiliares sólo son accesibles desde el propio método en que están.
Programación G-ISTSI 88
3.6.1
Los campos son privados
Los campos guardan el estado de un objeto y han de ser siempre privados. Por ejemplo, el campo
laVelocidad de la clase Coche
4
Introducción a la algorítmica
Problema algorítmico Relación entre unos datos de entrada y otros de salida establecida me-
diante las condiciones que deben cumplir los datos de salida.
Los datos de salida son así llamados porque dependen de los datos de
entrada.
• Así pues, los datos de salida de un problema se calculan a partir de los de entrada. Un algoritmo
es una de las formas en que el cálculo puede realizarse.
• La única exigencia para los pasos de un algoritmo es que sean no ambiguos.
En ciertos contextos no hay inconveniente en utilizar pasos “muy generales”. En cambio, en
otras ocasiones se necesitará que los pasos sean “muy concretos”.
Por ejemplo, un paso (muy general) de un algoritmo puede ser “calcular la derivada del
polinomio p(x)”. En otro momento, tal vez haya que tomar ese paso como un sub-problema
y refinarlo en pasos más elementales como operaciones aritméticas simples (sumas, ...).
• Los algoritmos son independientes del lenguaje de programación: un algoritmo puede imple-
mentarse de formas distintas en el mismo o en diferentes lenguajes de programación.
• Para definir un problema indicaremos
– un nombre que identifique el problema
– cada uno de los datos de entrada, incluyendo de qué tipo es y si debe cumplir alguna
condición
– cada uno de los datos de salida, incluyendo de qué tipo es y las condiciones que debe
cumplir
Opcionalmente pueden incluirse descripciones adicionales y algún ejemplo.
Programación G-ISTSI 92
4.1
Ejemplos
4.1.1
Máximo común divisor (mcd)
• Definición del problema:
– Datos de entrada:
a : Entero, a > 0
b : Entero, b > 0
– Datos de salida:
m : Entero, m = max{s|a mod s = 0 ∧ b mod s = 0}
Es evidente la relación que hay entre la definición de un problema y una función 12 que lo
resuelva.
• Ejemplos 1−1→0
a = 8, b = 6 ⇒ m = 2 • Observamos que
a = 13, b = 4 ⇒ m = 1 – se repite la operación
a = 21, b = 14 ⇒ m = 7 may − men → r
• ¡Idea! aplicar restas. – Inicialmente
– si a = 8, b = 6 may ← max(a, b)
8−6→2 men ← min(a, b)
6−2→4 – Después de realizar may − men → r, y
4−2→2 para la vez siguiente
2−2→0 may ← max(men, r)
– si a = 13, b = 4 men ← min(men, r)
13 − 4 → 9 – Dejamos de repetir la resta cuando
9−4→5 may = men o equivalentemente
5−4→1 repetimos mientras may 6= men
4−1→3 – Al dejar de repetir may (o men) es la
3−1→2 solución.
2−1→1
Programación G-ISTSI 95
m ← may
El paso may ← max(men, r) (y similarmente min()) puede refinarse
si men > r
may ← men
si no
may ← r
Programación G-ISTSI 96
4.1.2
sin(x)
• Definición del problema:
– Datos de entrada:
a : Real
– Datos de salida:
s : Real, s = longitud del cateto opuesto al ángulo a en un triángulo rectángulo de
hipotenusa = 1.
Una función para resolver este problema tendrá el siguiente diseño.
• No hay que asustarse ante una fórmula como la anterior. Es muy sencillo entenderla si la
escribimos “desplegada”:
a3 a5 a7 a9
a− + − + ...
3! 5! 7! 9!
• Es importante darse cuenta que la anterior fórmula es el algoritmo. Observemos que
– cada sumando es un cálculo de la forma
potencia
signo ×
f actorial
– Tras haber calculado un sumando, se acumula al resultado que se tiene hasta el momento
– Inicialmente signo = 1, potencia = a y f actorial = 1
– Para el cálculo del siguiente sumando hay que cambiar
signo → −signo
potencia → potencia · a · a
f actorial → f actorial ·2i·((2i)+1) (si tenemos que calcular el sumando i ∈ [0, 1, 2, . . .])
Programación G-ISTSI 98
signo ← −signo
potencia ← potencia · a · a
f actorial ← f actorial · 2i · ((2i) + 1)
s ← resultado
N es una constante con la que elegimos la cantidad de sumandos que queremos calcular.
Programación G-ISTSI 99
4.1.3
Ordenar
• Definición del problema:
– Datos de entrada:
numeros : lista de N números enteros. El i-ésimo entero puede consultarse con numeros[i]
(i ∈ [0, 1, . . . , N − 1]).
– Datos de salida:
numeros: lista de N números enteros (los mismos que los de entrada), cumpliendo ahora
que
numeros[0] ≤ numeros[1] ∧
numeros[1] ≤ numeros[2] ∧
numeros[2] ≤ numeros[3] ∧
numeros[3] ≤ numeros[4] ∧
... ∧
numeros[N − 3] ≤ numeros[N − 2] ∧
numeros[N − 2] ≤ numeros[N − 1]
es decir
numeros[i] ≤ numeros[i + 1] (i ∈ [0, 1, . . . , N − 2]).
Programación G-ISTSI 100
4.1.3.1
Algoritmo de selección
• Seguramente sea éste el más intuitivo: se basa en buscar el menor y ponerlo al principio de
la lista. Luego, buscar el segundo menor y ponerlo en el segundo lugar de la lista, buscar el
tercer menor y ponerlo en el tercer lugar de la lista, etc.
• En primer lugar, dado que hay que trabajar con una única lista (numeros), sus elementos
deben reordenarse in-situ. Por eso “poner al principio” significa intercambiar la posición de
dos elementos. De ahí que “buscar el menor” sea en realidad “buscar dónde está el menor”.
• Si por ejemplo hemos de “buscar dónde está el tercer menor” no hace falta mirar en toda la
lista. Si ya hemos ordenado el primero y el segundo, nos bastará con buscar a partir de la
tercera posición. Además, el intercambio será con la casilla a partir de la que hemos empezado
a buscar.
Programación G-ISTSI 101
Ahora hemos de refinar “buscar dónde está el menor ...” y “intercambiar ...”.
Programación G-ISTSI 102
Para conseguir
posM enor ← buscar dónde está el menor empezando en la casilla i
se compara cada elemento con un candidato a posM enor. Cuando se encuentre una casilla con
un elemento menor que numeros[posM enor] cambiaremos el candidato. Es decir:
posM enor ← i // el primer candidato es i
si numeros[posM enor] > numeros[i + 1], posM enor ← i + 1
si numeros[posM enor] > numeros[i + 2], posM enor ← i + 2
...
si numeros[posM enor] > numeros[N − 1], posM enor ← N − 1
Programación G-ISTSI 103
posM enor ← j
Programación G-ISTSI 104
Uniendo todas las piezas tenemos el algorimo completo de ordenación por selección:
i
0 −−−−−−−−−−−−−→ N − 2
//posM enor ← buscar dónde está el menor empezando en la casilla i
posM enor ← i
j
i+1 −−−−−−−−−−−−−→ N − 1
si numeros[posM enor] > numeros[j]
posM enor ← j
4.1.3.2
Algoritmo de la burbuja
Este algoritmo se basa en comparar casillas consecutivas e intercambiar su contenido si no están
correctamente ordenadas. En concreto:
comparar casillas 0 y 1, e intercambiar si no están ordenadas
comparar casillas 1 y 2, e intercambiar si no están ordenadas
comparar casillas 2 y 3, e intercambiar si no están ordenadas
...
comparar casillas N − 2 y N − 1, e intercambiar si no están ordenadas
Al realizar lo anterior una vez, podemos comprobar que un número va subiendo como una
burbuja mientras encuentra números menores en su camino. Al final, el mayor de todos habrá
llegado al final.
Programación G-ISTSI 107
intercambiar casillas j y j + 1
Programación G-ISTSI 108
Ya hemos visto que al realizar la burbuja una vez, llevamos el mayor al final. Si lo volvemos a
hacer, conseguiremos que “el segundo mayor” llegue a la penúltima casilla, y así sucesivamente.
Por tanto, para ordenar la lista completa haremos la burbuja terminando cada vez una casilla
antes (porque cada vez tenemos más números ordenados en la parte final).
hacer la burbuja de la casilla 0 a la N − 2
hacer la burbuja de la casilla 0 a la N − 3
hacer la burbuja de la casilla 0 a la N − 4
...
hacer la burbuja de la casilla 0 a la 2
hacer la burbuja de la casilla 0 a la 1
hacer la burbuja de la casilla 0 a la 0
Si plegamos el anterior bucle obtenemos:
i
N-2 −−−−−−−−−−−−−→ 0
hacer la burbuja de la casilla 0 a la i
Programación G-ISTSI 109
Sólo queda unir las dos partes para obtener el algoritmo de la burbuja completo:
i
N-2 −−−−−−−−−−−−−→ 0
// hacer la burbuja de la casilla 0 a la i
j
0 −−−−−−−−−−−−−→ i
si numeros[j] > numeros[j + 1]
intercambiar casillas j y j + 1
Programación G-ISTSI 110
4.1.3.3
Algoritmo de inserción
Supongamos que la lista de números tiene una parte (la izquierda) ordenada con respecto su
contenido. Por ejemplo:
2, 3, 8, 9 k 7, 1, 5
Como se ve, la parte 2, 3, 8, 9 esta ordenada con respecto a sí misma.
Para continuar con la ordenación, tendremos que insertar el 7 entre el 3, 8; y para logralo
usaremos el algoritmo de la burbuja empezando donde está el 7 y yendo hacia la izquierda: al
comparar 9 y 7 los intercambiamos, al comparar 8 y 7 los intercambianos, al comparar 3 y 7 ya
no hay intercambio: el 7 ha quedado donde le corresponde, y la lista será
2, 3, 7, 8, 9 k 1, 5
con lo que la parte ordenada ha crecido. Ahora habría que seguir con la inserción del 1, y luego
la del 5.
Programación G-ISTSI 111
Ahora se trata de escribir el algoritmo “insertar el número de la casilla i” Para que sea más fácil
pensemos en un ejemplo concreto:
2, 3, 8, 9 k 7, 1, 5
donde i = 4 y numeros[i] = 7
Haríamos:
comparar casillas 3 y 4, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 2 y 3, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 1 y 2, intercambiar si no estan ordenadas, parar si lo están
comparar casillas 0 y 1, intercambiar si no estan ordenadas, parar si lo están
En el ejemplo concreto 2, 3, 8, 9 k 7, 1, 5, hubiéramos parado al comparar las casillas 2 y 1 (es
decir el 7 con el 3).
Programación G-ISTSI 113
En cualquier caso, al plegar los pasos anteriores (teniendo en cuenta que la casilla 4 es i)
tendremos:
// insertar el numero de la casilla i
j
i −−−−−−−−−−−−−→ 1
si numeros[j − 1] > numeros[j]
intercambiar casillas j − 1 y j
si no
intercambiar casillas j − 1 y j
si no
4.2
Algoritmos y coste computacional
Al ver los tres algoritmos de ordenación anteriores (selección, burbuja e inserción), surgen las
siguientes preguntas:
• Los tres tienen dos bucles anidados, se basan en comparar e intercambiar, y por su puesto
ordenan. ¿Son algoritmos esencialmente diferentes o son el mismo algoritmo?
La respuesta es que efectivamente son algoritmos distintos –aunque ordenen y se utilicen las
mismas operaciones básicas. Una forma intuitiva de verlo es representar gráficamente cómo
realizan las comparaciones.
Programación G-ISTSI 116
• Entonces, si son algoritmos diferentes, ¿son igual de rápidos o hay alguno más veloz?
Para medir “la velocidad” de un algoritmo no podemos utilizar un cronómetro. Con un re-
loj podemos medir la velocidad de la ejecución de un programa escrito en un lenguaje de
programación concreto y ejecutado en un ordenador particular.
Pero lo que queremos saber es cómo de rápido es un algoritmo, independientemente de si se
ejecuta en un ordenador viejo o nuevo, porque el algoritmo es en ambos casos el mismo.
Así, lo que se hace para medir la velocidad de un algoritmo es contar la cantidad de
“operaciones elementales” que realiza. En nuestro caso las operaciones elementales son las
comparaciones y los intercambios. Además hay que fijarse en cuál es el punto del programa
que más “peso” aporta al algoritmo. Es evidente que como los tres algoritmos tienen dos
bucles anidados, las operaciones que más se repiten son las de dentro del bucle interno.
Programación G-ISTSI 117
intercambiar casillas j y j + 1
1 + 2 + 3 + ... + N − 3 + N − 2 + N − 1
=
N
X −1
i
i=1
=
N (N − 1)/2
=
1 2 1
N − N
2 2
Así pues, tenemos que 12 N 2 − 12 N es la cantidad de veces que se ejecuta la comparación más
repetida del algoritmo. Por simplicidad se dice que el coste es “del orden de N 2 ” que es el
término de mayor peso en la expresión.
Programación G-ISTSI 119
4.2.1
Algoritmo quicksort
Este algoritmo se basa en tomar un número como referencia (pivote) y situar a su izquierda los
que sean menores o iguales que él y a su derecha los que sean mayores. Por ejemplo, si la lista
de números es
10, 15, 7, 3, 13, 18, 1, 4, 6, 8, 11, 12, 19, 5
y tomamos el 10 como pivote, entonces la reordenación dejaría la lista de esta forma:
5, 8, 6, 4, 1, 3, 7, 10 , 15, 13, 18, 11, 12, 19
¿Qué hemos conseguido? Ordenar el 10.
Ahora podemos seguir aplicando la ordenación de forma separada a cada sub-lista. Es decir
3, 1, 4, 5 , 8, 6, 7, 10 , 11, 13, 15 , 18, 19
Programación G-ISTSI 120
Pero al terminar esta fase hemos ordenado 2 números en vez de sólo 1. También hemos obtenido
4 sublistas. Lo cual significa que, si seguimos, ordenaremos 4 números en vez de 2, y obtendremos
8 sublistas.
¿Qué cuesta realizar una reordeanción?:
Cuando la lista tiene N elementos hay que hacer N comparaciones.
Cuando tenemos 2 sublistas de N/2 elementos, cada sublista necesita N/2 comparaciones. Como
son 2 sublistas, N/2 + N/2 da N comparaciones.
En general, aunque las sublistas tienen cada vez la mitad de elementos, también es cierto que hay
el doble de sublistas, con lo que todas las reordenaciones juntas suman siempre N comparaciones.
La clave es que en cada fase ordenamos tantos números como sublistas hay. Así pues ¿cuántas
fases necesitamos realizar para ordenar todos los números? Vamos a contarlo:
• ...
• Fase i, 2i−1 sublistas, 2i−1 números ordenados. 1 + 2 + 4 + 8 + ... + 2i−1 = 2i − 1 números
ordenados en total (N comparaciones)
Es decir, con i fases ordenamos 2i números. ¿Cuántas fases necesitamos para ordenar N números?
Resolvamos i de la expresión 2i = N : i = log2 N . Es decir necesitamos log2 N fases.
Finalmente, el coste del algoritmo quicksort es
N log2 N
(número de comparaciones en cada fase por número de fases).
La velocidad del quicksort (N log2 N ) es abismalmente más rápida que la de los otros tres
algoritmos (N 2 ). Si hubiera que ordenar 1000 números y cada comparación costara un segundo,
el quicksort tardaría aproximadamente 6900 segundos, es decir casi 2 horas. Por su parte, los otros
algoritmos tardarían 1 millón de segundos: aproximadamente 11 días y medio.
Programación G-ISTSI 122
5
Relaciones entre clases
• En orientación a objeto, las relaciones básicas que pueden darse entre las clases que componen
un programa son:
– tener
– ser (herencia)
Programación G-ISTSI 123
5.1
Tener
• La relación tener se da entre dos clases cuando una posee a la otra, habiendo los siguientes
matices:
– composición (Coche tiene un Motor)
– agregación (Coche tiene un Conductor)
– contener (Parking tiene Coches)
• Como hemos visto, la relación tener se expresa en Java declarando un campo.
public class Coche extends Vehiculo {
...
private Motor elMotor; // Coche tiene Motor
...
}
• Es obvio que si objeto A tiene a otro B, entonces A usará a B de alguna forma. Una forma
importante de uso es la delegación.
Programación G-ISTSI 125
5.2
Delegación
• La delegación se da cuando un objeto a (delegador) usa a otro b (delegado), para que éste
último haga todo o parte del trabajo de a.
• Evidentemente se trata de un patrón absolutamente fundamental y ampliamente usado.
• Por ejemplo, supongamos que un Coche tiene Motor, y que ambos tienen el método acelera().
Entonces, cada vez que se llame a Coche.acelera() éste llamará a Motor.acelera():
public class Coche extends Vehiculo {
...
private Motor elMotor; // Coche tiene Motor
...
public void acelera (int incremento) {
elMotor.acelera( incremento );
}
...
}
Programación G-ISTSI 126
5.3
Herencia
• La herencia o relación ser un es un mecanismo 14 que permite que una clase aproveche el código
de otra.
14 Distinto de la delegación.
Programación G-ISTSI 127
• Como hemos visto, la herencia se expresa en Java con la cláusula extends. En Java la herencia
es simple: sólo se puede heredar de una clase.
• La primera acción de cualquier constructor de una clase derivada debe ser llamar a un cons-
tructor de la clase base mediante super.
public class Coche extends Vehiculo {
...
public Coche (String matricula) {
super (matricula);
}
...
}
5.3.1
Principales motivos para utilizar herencia
• Factor común.
Si varias clases (p.ej. Coche, Moto, Bicicleta) tienen un método común (dimeVelocidad()),
podemos escribir sólo una vez ese método en una clase base (Vehiculo) y hacer que las clases
(Coche, Moto, Bicicleta) deriven de ella (Vehiculo).
Programación G-ISTSI 129
• Especialización
Si tengo una clase javax.swing.JButton 15 y en mi programa quiero que los botones tengan
un estilo propio (p. ej. fondo azul y letra naranja), lo correcto es
public class MiBoton extends JButton {
public MiBoton (String texto) {
super (texto);
super.setBackground (Color.BLUE);
super.setForeground (Color.ORANGE);
... // mas particularidades que pueda necesitar
}
}
• Polimorfismo.
Si para realizar una determinada tarea (p.ej. viajar) nos sirve cualquier clase (Coche o Moto)
que tenga unos determinados requisitos (que sea un Vehiculo y que tenga el método acelera()),
entonces, la herencia permite garantizar que todo encaja: cualquier objeto de tipo Coche o
Moto será de tipo Vehiculo y tendrá el método acelera() porque lo habrán heredado.
Así podremos hacer:
void viajar (Vehiculo v) {
v.acelera(120);
}
...
void f() {
Coche c = new Coche ("1234ABC");
Moto m = new Moto ("78DF");
viajar (c);
viajar (m);
}
Programación G-ISTSI 132
5.3.2
Cambios de implementación
• No es raro que una clase derivada necesite cambiar la implementación de alguno de los métodos
heredados.
• Para ello se sobrescribe el método heredado: con exactamente el mismo perfil y diferente
código. Por ejemplo, si tenemos la clase base Vehiculo
public class Vehiculo {
private String laMatricula;
private int laVelocidad;
public Vehiculo (String matricula) {
// constructor
this.laMatricula = matricula;
this.laVelocidad = 0;
} // ()
public void acelera(int incremento) {
laVelocidad = (laVelocidad+incremento < 0 ? 0 : laVelocidad+incremento);
} // ()
...
} // class
5.3.3
Contratos de implementación
• Es útil disponer de un mecanismo que verifique que un diseño es implementado correctamente.
• Para ello, en Java, podemos escribir un interfaz que define una lista de métodos que deseamos
que sean implementados.
• Un interfaz es un contrato.
Por ejemplo, podríamos decidir que Vehiculo en vez de ser una clase ordinaria fuera un
interfaz con los métodos que queremos que tenga cualquier vehículo.
public interface Vehiculo {
String dimeMatricula ();
int dimeVelocidad ();
void acelera (int incremento);
}
Programación G-ISTSI 135
y entonces
– el compilador emitirá un error si las clases Coche o Moto no tienen los métodos del interfaz
Vehiculo
– un objeto de tipo Coche o Moto será también de tipo Vehiculo.
– podremos usar el interfaz como requisito en un contexto de polimorfismo. Recordemos:
void viajar (Vehiculo v) {
v.acelera(120);
}
void f() {
Coche c = new Coche("1234ABC");
Moto m = new Moto("78DF");
// c y m son de tipo Vehiculo, por tanto:
viajar (c);
viajar (m);
}
Programación G-ISTSI 136
• La relación entre el interfaz Vehiculo y las clases Coche y Moto se puede representar así:
Programación G-ISTSI 137
5.3.3.1
Implementaciones parciales
• Hay ocasiones en que se necesita una clase parcialmente implementada: algunos métodos
tienen código y otros no.
• De esta forma, la clase sirve de contrato (obliga a implementar los métodos sin código) pero
también deja en herencia los métodos implementados.
• Una clase parcialmente implementada se llama clase abstracta. Los métodos sin código se
denominan abstractos.
• Un clase abstracta no se puede instanciar. Hay que escribir una clase que derive de ella y
complete los métodos abstractos.
• En Java, un interface sería un clase asbstracta con todos los métodos abstractos.
Programación G-ISTSI 138
5.3.4
Sustituibilidad: herencia correcta
• Cuando una clase hereda de otra y rescribimos un método (ver 5.3.2) se corre el peligro de que
la nueva implementación rompa el contrato que se tenía hasta ese momento, y el programa
deje de funcionar correctamente.
• Por ejemplo, hasta ahora hemos utilizado el método Coche.acelera() tanto para aumentar
la velocidad como para reducirla (dando un valor negativo).
Si creamos una nueva clase Bolido que derive de Coche, en la cual:
– cambiamos el método acelera() para que sólo aumente la velocidad
– añadimos un método frena() para reducir la velocidad
entonces, tenemos un problema: un objeto Bolido pese a ser de tipo Coche no puede ser
utilizado en el programa porque nada del código anterior sabe de la existencia del método
frena() y lo que es peor, utilizan acelera() con valores negativos para frenar (cosa que
Bolido no soporta).
Programación G-ISTSI 141
5.3.5
Objetos polifacéticos
• Al utilizar la herencia un objeto tiene varios tipos.
• Ya hemos visto que si Coche hereda de Vehiculo, entonces, un objeto de tipo Coche es
también de tipo Vehiculo.
• Más aún, si Coche implementa los interfaces CocheDeTrabajo y CocheFamiliar entonces,
un objeto de tipo Coche es también de tipo CocheDeTrabajo y CocheFamiliar.
• La anterior situación puede representarse así:
Programación G-ISTSI 143
• Hay que tener en cuenta que un objeto de tipo Coche es un único objeto, pero dado el anterior
diseño tiene varias facetas y según cómo se le referencie se puede ver unas u otras:
Si apuntamos al objeto
– con una referencia de tipo Coche veremos todos los métodos (los de Vehiculo, CocheFammiliar,
CocheDeTrabajo y otros que pudiera tener la clase Coche).
– con una referencia de tipo Vehiculo sólo veremos los métodos dimeMatricula(), dimeVelocidad()
y acelera().
Programación G-ISTSI 144
– con una referencia de tipo CocheFamiliar sólo veremos los métodos salirDePaseo() y
hacerLaCompra().
– con una referencia de tipo CocheDeTrabajo sólo veremos los métodos irAlTrabajo() y
estarEnUnAtasco().
Programación G-ISTSI 145
5.3.5.1
Casting
• Hemos visto que un objeto es a la vez de varias clases y que según el tipo de la referencia con
que se le apunte podremos utilizar unos u otros métodos.
• Por ejemplo si apuntamos a un objeto de tipo Coche con una referencia de tipo CocheFamiliar
sólo veremos los métodos salirDePaseo() y hacerLaCompra().
• Pero nosotros sabemos que tiene otros métodos. Si queremos utilizarlos hemos de hacerlo
mediante una referencia de tipo diferente.
• La asignación de una referencia de un tipo a otro, en general, no está permitida, excepto que
se utilice un casting: un aviso escrito por nosotros para que el compilador no dé error.
• El casting no afecta a los objetos, sino a las referencias.
Programación G-ISTSI 146
Tipos de casting:
• El casting es innecesario si asignamos algo con un tipo de menor precisión a uno con mayor
precisión. Por ejemplo:
int a = 3;
double x;
x = a; // de entero a real, OK
En cambio en el sentido inverso hay que escribir un casting para dar a entender que somos
conscientes de la pérdida de información:
a = (int) x; // de real a entero, necesito casting
• downcasting El casting es necesario cuando asignamos una referencia de tipo base (Vehiculo
a una de tipo derivado (Coche): la referencia de tipo Vehiculo podría no estar apuntando a
un objeto Coche.
Coche c;
Vehiculo v;
...
c = (Coche) v; // casting necesario
instanceof
5.4
Tenencia y delegación vs. herencia
• Recordemos que la relación tener permite construir un objeto uniendo partes (otros objetos)
y utilizándolos para que realicen el trabajo (delegación).
En la herencia, el código de un objeto base entra a formar parte intrínseca del objeto derivado.
• Es un error común “sobre-utilizar” la herencia, es decir, utilizarla cuando no se necesita o de
forma errónea.
• La herencia sólo debe utilizarse si está claro que un objeto derivado es también de tipo base:
Coche es Vehiculo pero Motor no. Coche tiene Motor.
• En general, es preferible utilizar la delegación a la herencia. En la herencia, la clase derivada
conoce muchos detalles de la parte privada de la clase base, y acaban apareciendo dependencias
innecesarias: lo deseable es utilizar sólo las partes públicas.
• Si nos centramos demasiado en la herencia terminaremos sobre-diseñando: creando árboles de
herencia innecesariamente amplios.
Por ejemplo, podríamos diseñar clases como VehiculoAMotor, VehiculoSinMotor, CocheFamiliar,
Bolido, etc. que aunque las hayamos utilizado en los ejemplos, puede que en un caso prácti-
co sean no sólo innecesarias sino que introduzcan complejidad evitable. Si el programa puede
realizarse con Vehiculo, Coche y Moto, mejor: Keep it as simple as possible, no more.
Programación G-ISTSI 150
5.5
Clases anónimas
Hay ocasiones en que se necesita modificar ligeramente una clase existente (rescribir uno o dos
métodos) para crear un objeto de forma puntual.
Tener que escribir la nueva clase en un fichero separado resulta incómodo.
Java permite en un sólo paso crear e instanciar una nueva clase “in situ” 16. La nueva clase se
crea a partir de otra existente –herencia– o incluso a partir de un interfaz.
Un ejemplo real.
unBoton.addActionListener (
new ActionListener () {
public void actionPerformed (ActionEvent ev) {
System.out.println ("soy el boton hola: " + valor);
} // actionPerformed()
} // class
); // addActionListener
• Nota: desde el código de un clase anónima se puede acceder campos de la clase pero no
variables locales del método en que se crea 17.
5.6
Un ejemplo: Formas
Veamos un diseño simplificado y parcial de lo que podría ser una aplicación de dibujo sencilla.
Programación G-ISTSI 153
6
Excepciones
En 2.5 introdujimos las excepciones. Recordemos:
• Situación anómala que puede ocurrir en un programa y frente a la que puede reaccionarse
capturándola o traspasándola (para que sea capturada en otra parte del programa).
• Sabemos que un método puede fallar porque en su perfil aparece una cláusula throws. Por
ejemplo:
public static int parseInt(String s) throws NumberFormatException
Programación G-ISTSI 156
6.1
Escribir nuevas excepciones
• Escribir una nueva excepción debe estar justificado por el diseño del programa que estemos
desarrollando. Además, conviene comprobar si en la biblioteca estándar hubiera alguna excep-
ción que nos pueda servir.
Es muy simple: lo importante es elegir un nombre representativo para la clase (en vez de
MiExcepcion).
Es obligatorio escribir un constructor que reciba un string y que lo reenvie al código he-
redado (para que se lo guarde). Más tarde se podrá consultar ese mensaje con el método
getMessage().
En la mayoría de casos, sólo por el nombre de la excecpción sabremos qué ha pasado, sin
necesidad de utilizar getMessage(). Otro método disponible (heredado de Exception) es
Programación G-ISTSI 158
7
Programación genérica. Plantillas.
Supongamos que queremos escribir una clase llamada Caja para guardar dos valores enteros.
class Caja {
private int primero;
private int segundo;
public Caja (int pri, int seg) {
primero = pri;
segundo = seg;
} // ()
int getPrimero () {
return primero;
} // ()
int getSegundo () {
return segundo;
} // ()
} // class
Es evidente que si en vez de dos enteros fueran dos números reales (o cualquier otro tipo), el
único cambio que necesita la clase Caja es reemplazar int por double.
Programación G-ISTSI 160
Por este motivo aparecen las plantillas: meta-clases que se escriben de forma genérica depen-
diendo de un tipo T que más tarde se dirá cuál es. Así, con una plantilla se pueden crear fácilmente
muchas clases similares.
En nuestro ejemplo, para conseguir Caja<T>: una caja genérica que contiene dos T, escribiremos
class Caja<T> {
private T primero;
private T segundo;
public Caja (T pri, T seg) {
primero = pri;
segundo = seg;
} // ()
T getPrimero () {
return primero;
}
T getSegundo () {
return segundo;
}
} // class
Es importante destacar que para indicar qué tipo concreto es el parámetro T de una plantilla no
se puede utilizar tipos básicos (int, double, etc.). Hay que utilizar el envoltorio correspondente
(Integer, Double, etc. Ver paquete java.lang).
El diseño de la plantilla Caja<T> es el siguiente:
Programación G-ISTSI 162
7.1
Exigiendo requisitos a un tipo de plantilla
La plantilla Caja<T> anterior puede instanciarse con cualquier tipo, porque las operaciones que
Caja<T> hace con T son guardar y devolver, que son soportadas universalmente.
¿Qué ocurría si Caja<T> necesitara que T tuviera el método convertirAEntero()?
Para obligar a que T tenga el método convertirAEntero(), se escribe un interfaz que exprese
esa obligación:
interface ConvertibleAEntero {
int convertirAEntero();
}
} // class
Programación G-ISTSI 164
7.2
Interfaces plantilla
Un interfaz también puede estar parametrizado y de hecho en muchas ocasiones necesitaremos
que lo esté.
Volvamos al ejemplo de Caja<T> y su pongamos ahora que pretendemos añadirle un método
sumaGuardados() que devuelva la suma de lo que guarda:
public T sumaGuardados () {
return primero.suma(segundo);
}
Como se ve, necesitamos que la clase T tenga el método suma(), que reciba algo de tipo T y
que (T + T → T ) devuelva T.
Esto se consigue con el siguiente interfaz plantilla:
interface Sumable<T> {
public T suma (T otro)
}
Programación G-ISTSI 165
Si quisierámos que el tipo T fuera la clase Real, haríamos que tuviera el método Real suma
(Real otro):
class Real implements Sumable<Real> {
...
Real suma (Real otro) {
...
}
} // class
Programación G-ISTSI 166
7.3
Una Caja<Coche> no es una Caja<Vehiculo>
Efectivamente. Aunque Coche herede de Vehiculo una Caja<Coche> no es una Caja<Vehiculo>
porque Caja<Coche> no puede sustituir a Caja<Vehiculo>:
en una Caja<Vehiculo> podemos guardar un Coche y una Moto, pero en una Caja<Coche> sólo
podemos guardar coches.
Entonces, si la clase base de Caja<Coche> no es de tipo Caja<Vehiculo>, ¿de qué tipo es?:
de tipo Caja<?>
De esta forma se puede escribir un método que reciba cualquier tipo de Caja:
void metodoQueRecibeCualquierCaja (Caja<?> ca) {
...
}
Programación G-ISTSI 167
Si queremos un método que reciba una caja que contega algo derivado de Vehiculo:
void miMetodo (Caja<? extends Vehiculo> caj) {
...
}
y también sirve
public static <T extends Vehiculo> void m1 (Caja<T> ca) {
...
}
Programación G-ISTSI 168
7.4
Algoritmos genéricos
Hay un importante principio de diseño que dice que hay que separar un contenedor de los
algoritmos que se aplican sobre él.
Esto permite
7.4.1
Algoritmo “para cada” sencillo
Se trata de un método estático Para.cada() 18 que aplica una función genérica a cada elemento
de una colección.
La definición es la siguiente:
//............................................
abstract class Funcion <T> {
public abstract void visita (T v);
public Object getResultado () { return null; }
} // class
//............................................
class Para {
public static <T> void cada (Collection <T> c, Funcion<T> f) {
for (T v : c) {
f.visita (v);
}
} // ()
} // class
18 El nombre de la clase Para y del método cada() forman un juego de palabras para que quede la frase “para cada”.
Programación G-ISTSI 170
Como se ve, el anterior código, calcula la suma de longitudes de los strings guardados en el
vector palabras. Algo que puede hacerse simplemente con:
int total = 0;
for (String s : palabras) {
total = total + s.length();
}
Sin embargo la versión Para.cada() tiene la pequeña ventaja de haber creado un objeto
Funcion<String> fun que engloba lo que se pretende hacer con cualquier string que éste reciba,
siendo fun fácilmente reutilizable, y utilizable: Para.cada (palabras, fun);
Programación G-ISTSI 172
7.4.2
Visitador
Un visitador a una colección es esencialmente un método Para.cada() con la variación de que
permite aplicar una función distinta según el tipo concreto del elemento guardado en la colección.
A continuación vemos la definición del nuevo Para.cada().
//............................................
abstract class Visitador<T> {
public void Visita(T n) {
try {
this.getClass().getDeclaredMethod("hazlo", n.getClass()).invoke(this, n);
} catch (Exception ex) {
hazlo((T) n);
}
}
public void hazlo(T n) {
System.out.println("hazlo() para base " + n);
}
public Object getResult() {
return null;
}
} // class
Programación G-ISTSI 173
//............................................
class Para {
public static <T> void cada (Collection<T> c, Visitador<T> f) {
for (T v : c) {
f.Visita(v);
}
} // ()
} // class
numeros.add(new Double(1.2));
numeros.add(new Float(-1.2));
numeros.add(new Double(4.8));
numeros.add(new Float(-3.4));
numeros.add(new Long(123456));
numeros.add(new Short("14"));
Programación G-ISTSI 174
7.4.3
Buscador
En este ejemplo mostramos como escribir una función genérica que busque en una colección los
elementos que cumplan Buscar.losQueCumplen() (o los que no: Buscar.losQueNoCumplen())
un predicado, produciendo una nueva colección.
He aquí las definiciones
//............................................
interface Predicado <T> {
boolean esCierto (T v);
} // class
//............................................
class Buscar {
public static <T> Collection<T> losQueCumplen (Collection <T> c, Predicado<T> pred) {
ArrayList<T> result = new ArrayList<T> ();
for (T v : c) {
if ( pred.esCierto (v) ) {
result.add (v);
}
} // for
return result;
} // ()
Programación G-ISTSI 176
} // class
7.5
Funciones con cantidad variable de argumentos
Aunque no se trate de plantillas, sí son en cierto modo una genericidad, ya que permiten,
escribiendo una sola vez un método, que éste puede ser invocado en distintas ocasiones con una
cantidad diferente de argumentos.
En el siguiente ejemplo vemos cómo se escribe un método que calcula la media de los números
que recibe, pudiendo estos variar en cantidad:
class Main {
public static double media (double ... args) {
double suma = 0;
for (double d : args) {
suma = suma + d;
}
return suma / args.length;
} // ()
public static void main (String[] args) {
double m;
m = media (1.2, 3.4, 4.5);
m = media (1.2, 3.4, 4.5, 6.7, 8.9);
} // ()
} // class
Programación G-ISTSI 179
Si se necesitara una función que reciba argumentos de distintos tipos, habría que recurrir a
Object ... args y después decidir qué hacer en función del tipo utilizando instanceof . Por
ejemplo:
class Main {
public static void f (Object ... args) {
int total = 0; StringBuffer sb = new StringBuffer();
for (Object o : args) {
if (o instanceof Integer) {
total += (Integer) o;
}
if (o instanceof String) {
sb.append (o + " ");
}
} // for
System.out.println (" suma = " + total);
System.out.println (" frase = " + sb);
} // ()