4.D Colecciones de objetos y miembros estáticos¶
Objetos que contienen objetos¶
Los objetos se pueden combinar: un curso tiene alumnos, una biblioteca tiene libros, una persona tiene cuentas. Es la relación de composición («tiene un»), y se resuelve guardando los objetos dentro de otro, normalmente en una lista.
Curso ◆────── 0..n ──────▶ Alumno
(guarda una lista) (nombre, nota)
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class Alumno {
static int creados = 0;
final String nombre;
final int nota;
Alumno(String nombre, int nota) {
if (!notaValida(nota)) {
throw new IllegalArgumentException("nota fuera de rango");
}
this.nombre = nombre;
this.nota = nota;
creados++;
}
static boolean notaValida(int nota) {
return nota >= 0 && nota <= 10;
}
boolean aprobado() {
return nota >= 5;
}
@Override
public String toString() {
return nombre + " (" + nota + ")";
}
}
class Curso {
private final List<Alumno> alumnos = new ArrayList<>();
void matricular(Alumno alumno) {
alumnos.add(alumno);
}
int total() {
return alumnos.size();
}
double media() {
int suma = 0;
for (Alumno a : alumnos) {
suma += a.nota;
}
return (double) suma / alumnos.size();
}
Alumno mejor() {
Alumno mejor = alumnos.get(0);
for (Alumno a : alumnos) {
if (a.nota > mejor.nota) {
mejor = a;
}
}
return mejor;
}
List<Alumno> aprobados() {
List<Alumno> resultado = new ArrayList<>();
for (Alumno a : alumnos) {
if (a.aprobado()) {
resultado.add(a);
}
}
return resultado;
}
}
public class Poo4 {
public static void main(String[] args) {
Curso curso = new Curso();
curso.matricular(new Alumno("Ana", 4));
curso.matricular(new Alumno("Marta", 8));
curso.matricular(new Alumno("Luis", 6));
System.out.println("Matriculados: " + curso.total());
System.out.println("Media del curso: " + curso.media());
System.out.println("Mejor alumno: " + curso.mejor());
System.out.println("Aprobados: " + curso.aprobados().stream()
.map(a -> a.nombre).collect(Collectors.joining(", ")));
try {
new Alumno("Error", 11);
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
System.out.println("Alumnos creados: " + Alumno.creados);
}
}
Salida:
Matriculados: 3
Media del curso: 6.0
Mejor alumno: Marta (8)
Aprobados: Marta, Luis
Error: nota fuera de rango
Alumnos creados: 3
Qué conviene copiar de este diseño:
- El curso decide qué se puede hacer con su lista. Ofrece
matricular,media,mejoryaprobados, pero no entrega la lista para que cualquiera la modifique.for (Alumno a : alumnos)o streams. La lista esprivate: desde fuera solo se puede matricular o consultar. - Cada clase se ocupa de lo suyo.
Alumnosabe si está aprobado;Cursosabe calcular la media y buscar al mejor.mainsolo orquesta. - Los recorridos son los de siempre (acumular una suma, buscar un máximo, filtrar), solo que ahora recorren objetos y consultan sus atributos y propiedades.
- Las reglas del objeto, en el objeto: la nota se valida en el constructor de
Alumno, así un alumno con nota 11 no llega a existir.
Miembros estáticos¶
Hasta ahora, cada atributo y cada método pertenecía a un objeto concreto. Un miembro estático pertenece a la clase, y es el mismo para todos los objetos. En el ejemplo hay dos:
creados: un contador compartido que cuenta cuántos alumnos se han creado (no tiene sentido guardarlo en cada alumno).notaValida: una función de utilidad que no necesita ningún alumno para funcionar.
En Java se marca con static: static int creados = 0; y static boolean notaValida(int nota). Se usa con el nombre de la clase: Alumno.creados, Alumno.notaValida(5). El método main es static precisamente porque se ejecuta sin crear ningún objeto.
No abuses de lo estático
Un atributo estático es estado global: cualquiera puede cambiarlo y los resultados dependen de lo que haya ocurrido antes en el programa, lo que complica las pruebas. Úsalo para contadores sencillos, constantes y utilidades; no para guardar los datos de la aplicación.
Errores frecuentes¶
| Error | Cómo evitarlo |
|---|---|
| Devolver la lista interna para que otros la modifiquen | Ofrece métodos concretos, o devuelve una copia o una vista de solo lectura |
Buscar el máximo empezando con un valor «inventado» (como 0) |
Empieza con el primer elemento (y comprueba que la lista no esté vacía) |
| Dividir por el tamaño de una lista vacía | Comprueba antes que hay elementos |
| Poner datos de objetos concretos en un atributo estático | Un atributo estático es para lo que es común a todos |
Para practicar¶
Las colecciones de objetos y las relaciones entre clases se practican en U4.3 · POO II. Pasar de uno a otro muestra las mismas ideas en otro lenguaje.