Comparable y Cloneable en Java — ejercicios con libros, jugadores y una plantilla clonada (y la resta que puso al más rico el primero)

Ordenaba cuentas bancarias por saldo y escribí, tan tranquilo, return this.saldo - otra.saldo; dentro de compareTo. Con los datos de clase iba perfecto: 100, 250, 4000… El día que probé con un saldo enorme y un descubierto enorme, la cuenta de dos mil millones salió la primera de una lista que debía ir de menor a mayor. Sin excepción, sin mensaje, sin nada: Java ordenó mal con toda su buena fe, porque la resta se había desbordado y mi compareTo mentía.

Es el tipo de fallo que no se aprende leyendo: se aprende rompiéndolo. Hoy practicamos con tres programas de dificultad creciente, tres fragmentos para depurar (uno es esa resta) y un chuletario para llevarte.

Estos ejercicios usan la teoría de Comparable y Cloneable y repasan lo de la práctica. Sin ellas puedes hacerlos, pero te costará más.

Cómo usar este artículo

  1. Lee el enunciado de un nivel y no mires la solución todavía.
  2. Escribe tu versión, compílala y compara tu salida con la esperada.
  3. Si te atascas, mira solo la 🔵 pista y vuelve a intentarlo.
  4. Cuando termines, compara con la solución y fíjate en por qué está hecha así, no solo en que dé la misma salida.

Ningún ejercicio usa Scanner. Los datos van fijos en el código para que tu salida sea idéntica a la esperada y puedas comprobarla de un vistazo.

Nivel 1 · Básico: libros ordenados por páginas

Enunciado. Crea una clase Libro con título y número de páginas (int), con sus getters y un toString que devuelva, por ejemplo, Platero y yo (160 págs.). Haz que Libro sea Comparable<Libro> con este orden natural: de menos a más páginas.

En una clase N1 con main, crea un array con estos cuatro libros, ordénalo con Arrays.sort e imprímelo:

  • El Quijote, 1376 páginas
  • Platero y yo, 160 páginas
  • La Celestina, 280 páginas
  • Fortunata y Jacinta, 1008 páginas

Después, muestra el más corto y el más largo (sin volver a ordenar: usa las posiciones del array ya ordenado).

Salida esperada:

De menos a más páginas:
  Platero y yo (160 págs.)
  La Celestina (280 págs.)
  Fortunata y Jacinta (1008 págs.)
  El Quijote (1376 págs.)
El más corto: Platero y yo (160 págs.)
El más largo: El Quijote (1376 págs.)

Una vez ordenado el array, el más corto está en la posición 0 y el más largo en estante.length - 1. Y para comparar enteros, nada de restas: Integer.compare.

Nivel 2 · Intermedio: la clasificación de una liga

Enunciado. Crea una clase Jugador con nombre, puntos y partidos jugados (los tres como atributos finales, con getters) y un toString con el formato Lía 150 pts en 10 partidos. No la hagas Comparable: el criterio de la clasificación será un Comparator aparte.

En N2, declara una constante static final Comparator<Jugador> CLASIFICACION que ordene por:

  1. Más puntos primero.
  2. Si empatan, menos partidos jugados primero (quien lo consiguió en menos partidos va por delante).
  3. Si siguen empatando, por nombre.

Con estos cinco jugadores en una lista, ordénala e imprime el top 3 numerado y el último:

NombrePuntosPartidos
Noa12010
Iker15012
Lía15010
Hugo908
Aitana12010

Salida esperada:

Top 3:
  1. Lía 150 pts en 10 partidos
  2. Iker 150 pts en 12 partidos
  3. Aitana 120 pts en 10 partidos
Último: Hugo 90 pts en 8 partidos

comparingInt(...).reversed() invierte solo lo que hay antes del reversed(). Colócalo justo después del primer criterio y añade los demás con thenComparing...: así los puntos van de más a menos pero los partidos, de menos a más.

Nivel 3 · Desafío: una plantilla que se clona de verdad

Enunciado. Un equipo tiene una plantilla de jugadores y un estadio. Queremos poder hacer una copia profunda de la plantilla para probar fichajes sin tocar la original. Crea tres clases, **todas Cloneable** y con clone() público y sin excepción comprobada:

  • Estadio: nombre y aforo (int, con setAforo). toString: La Isla (5000).
  • Jugador: nombre y puntos (int, con un método sumar(int)). toString: Noa:10.
  • Plantilla: equipo, lista de jugadores y estadio; métodos fichar(Jugador), getJugadores() y getEstadio(). toString: Tigres [Noa:10, Iker:12] en La Isla (5000).

El clone() de Plantilla debe ser profundo: la copia tiene su propia lista, sus propios jugadores y su propio estadio.

En N3, crea la plantilla «Tigres» con el estadio «La Isla» (5000) y los jugadores Noa (10) e Iker (12). Clónala y, en la copia solamente: ficha a Lía (7), súmale 100 puntos al primer jugador y cambia el aforo a 9000. Imprime las dos plantillas y si el primer jugador de cada una es el mismo objeto.

Salida esperada:

Original: Tigres [Noa:10, Iker:12] en La Isla (5000)
Copia:    Tigres [Noa:110, Iker:12, Lía:7] en La Isla (9000)
¿Mismo jugador en la posición 0? false

super.clone() te da una Plantilla con la misma lista y el mismo estadio. Reemplaza el campo de la lista por una lista nueva y, dentro de un bucle, añádele j.clone() de cada jugador. El estadio, con estadio.clone().

¿Qué falla aquí?

Tres fragmentos con un fallo cada uno. Antes de leer la explicación, haz dos cosas: predice si compila y, si compila, qué imprime (o qué pasa al ejecutarlo).

Fragmento 1

Cuenta.java

public class Cuenta implements Comparable<Cuenta> {
    private final String titular;
    private final int saldo;   // en céntimos

    public Cuenta(String titular, int saldo) {
        this.titular = titular;
        this.saldo = saldo;
    }

    @Override
    public int compareTo(Cuenta otra) {
        return this.saldo - otra.saldo;
    }

    @Override
    public String toString() { return titular + ": " + saldo; }
}

F1.java

import java.util.Arrays;

public class F1 {
    public static void main(String[] args) {
        Cuenta[] cuentas = {
            new Cuenta("Ana", 2_000_000_000),
            new Cuenta("Luis", -2_000_000_000),
            new Cuenta("Marta", 5)
        };
        Arrays.sort(cuentas);
        System.out.println(Arrays.toString(cuentas));
    }
}

Piénsalo antes de seguir: ¿compila? ¿qué imprime? Las cuentas deberían salir de menor a mayor saldo.

Explicación. Compila y no lanza ninguna excepción. Pero el resultado es este:

[Ana: 2000000000, Luis: -2000000000, Marta: 5]

El orden es incorrecto: Ana (dos mil millones) sale la primera y Luis (menos dos mil millones) en medio. La culpa es de la resta. Comparar a Ana con Luis calcula 2 000 000 000 - (-2 000 000 000) = 4 000 000 000, que **no cabe en un int** (el máximo es 2 147 483 647) y se desborda a un número negativo. compareTo le dice a Java que Ana va antes que Luis.

return this.x - otro.x en un compareTo de enteros. Funciona con valores pequeños y falla en silencio con valores grandes o de signo opuesto.

Arreglo. Deja que Java compare, sin restar:

@Override
public int compareTo(Cuenta otra) {
    return Integer.compare(this.saldo, otra.saldo);
}
[Luis: -2000000000, Marta: 5, Ana: 2000000000]

Integer.compare(a, b) (o Double.compare) existe precisamente para esto. Es lo mismo que la resta, pero sin desbordamiento.

Fragmento 2

Jugador.java

public class Jugador implements Comparable<Jugador> {
    private final String nombre;
    private final int puntos;

    public Jugador(String nombre, int puntos) {
        this.nombre = nombre;
        this.puntos = puntos;
    }

    @Override
    public int compareTo(Jugador otro) {
        return Integer.compare(this.puntos, otro.puntos);
    }

    @Override
    public String toString() { return nombre + " " + puntos; }
}

F2.java

import java.util.Arrays;

public class F2 {
    public static void main(String[] args) {
        Jugador[] plantilla = new Jugador[4];   // todavía no hemos fichado al cuarto
        plantilla[0] = new Jugador("Noa", 120);
        plantilla[1] = new Jugador("Iker", 150);
        plantilla[2] = new Jugador("Lía", 90);

        Arrays.sort(plantilla);
        System.out.println(Arrays.toString(plantilla));
    }
}

¿Compila? ¿qué imprime? ¿Qué pasa con la posición que está sin rellenar?

Explicación. Compila bien. Pero new Jugador[4] crea un array de cuatro **referencias a null**, y solo hemos rellenado tres. Arrays.sort intenta comparar todos los elementos, y compara con uno que no existe:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "pivot" is null
	at java.base/java.util.ComparableTimSort.binarySort(ComparableTimSort.java:262)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:189)
	at java.base/java.util.Arrays.sort(Arrays.java:1042)
	at F2.main(F2.java:10)

NullPointerException dentro de Arrays.sort o Collections.sort. El orden natural llama a compareTo sobre los elementos, y un null no tiene métodos. El fallo aparece lejos de donde pusiste el null (aquí, en el new Jugador[4]).

Arreglo. Dos opciones: ordenar solo la parte rellena o decirle a Java qué hacer con los null. La segunda, con un Comparator que los manda al final:

import java.util.Arrays;
import java.util.Comparator;

public class F2Fix {
    public static void main(String[] args) {
        Jugador[] plantilla = new Jugador[4];
        plantilla[0] = new Jugador("Noa", 120);
        plantilla[1] = new Jugador("Iker", 150);
        plantilla[2] = new Jugador("Lía", 90);

        Arrays.sort(plantilla, Comparator.nullsLast(Comparator.naturalOrder()));
        System.out.println(Arrays.toString(plantilla));
    }
}
[Lía 90, Noa 120, Iker 150, null]

Comparator.nullsLast(...) y nullsFirst(...) envuelven cualquier comparador para que sepa qué hacer con los null. Pero el mejor arreglo suele ser que el array no tenga huecos.

Fragmento 3

Jugador.java

public class Jugador implements Cloneable {
    protected String nombre;
    protected int puntos;

    public Jugador(String nombre, int puntos) {
        this.nombre = nombre;
        this.puntos = puntos;
    }

    @Override
    public Jugador clone() {
        return new Jugador(nombre, puntos);   // "crear uno igual", ¿no?
    }

    @Override
    public String toString() { return nombre + " " + puntos; }
}

Portero.java

public class Portero extends Jugador {
    private int paradas;

    public Portero(String nombre, int puntos, int paradas) {
        super(nombre, puntos);
        this.paradas = paradas;
    }

    @Override
    public Portero clone() {
        Portero copia = (Portero) super.clone();
        copia.paradas = this.paradas;
        return copia;
    }

    @Override
    public String toString() { return super.toString() + " y " + paradas + " paradas"; }
}

F3.java

public class F3 {
    public static void main(String[] args) {
        Portero original = new Portero("Noa", 120, 35);
        Portero copia = original.clone();
        System.out.println(copia);
    }
}

*¿Compila? ¿qué imprime? Fíjate en cómo se crea la copia en Jugador.clone().*

Explicación. Compila. Pero Jugador.clone() no usa super.clone(): fabrica la copia con new Jugador(...), así que siempre devuelve un Jugador normal, aunque el objeto que se clona sea un Portero. Cuando Portero.clone() intenta convertir ese resultado a Portero, el cast falla:

Exception in thread "main" java.lang.ClassCastException: class Jugador cannot be cast to class Portero (Jugador and Portero are in unnamed module of loader 'app')
	at Portero.clone(Portero.java:11)
	at F3.main(F3.java:4)

ClassCastException en un clone() de una subclase. La superclase crea la copia con new en lugar de super.clone(), y la copia pierde el tipo real.

Arreglo. En la superclase, usa super.clone(), que crea un objeto del tipo real del que se está clonando:

@Override
public Jugador clone() {
    try {
        return (Jugador) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new AssertionError(e);
    }
}
Noa 120 y 35 paradas

Regla de oro: dentro de cualquier clone(), la primera línea es super.clone(). Si ves un new ahí, desconfía.

Soluciones

Mira esta sección solo cuando hayas intentado el ejercicio. Si tu código es distinto pero da la misma salida y cumple los contratos, también es una solución válida.

Solución del Nivel 1

Libro.java

public class Libro implements Comparable<Libro> {
    private final String titulo;
    private final int paginas;

    public Libro(String titulo, int paginas) {
        this.titulo = titulo;
        this.paginas = paginas;
    }

    public String getTitulo() { return titulo; }
    public int getPaginas() { return paginas; }

    // Orden natural: de menos a más páginas
    @Override
    public int compareTo(Libro otro) {
        return Integer.compare(this.paginas, otro.paginas);
    }

    @Override
    public String toString() {
        return titulo + " (" + paginas + " págs.)";
    }
}

N1.java

import java.util.Arrays;

public class N1 {
    public static void main(String[] args) {
        Libro[] estante = {
            new Libro("El Quijote", 1376),
            new Libro("Platero y yo", 160),
            new Libro("La Celestina", 280),
            new Libro("Fortunata y Jacinta", 1008)
        };

        Arrays.sort(estante);

        System.out.println("De menos a más páginas:");
        for (Libro libro : estante) {
            System.out.println("  " + libro);
        }
        System.out.println("El más corto: " + estante[0]);
        System.out.println("El más largo: " + estante[estante.length - 1]);
    }
}

Por qué está hecho así:

  • Integer.compare(this.paginas, otro.paginas) en lugar de una resta: da el mismo resultado sin riesgo de desbordamiento.
  • Arrays.sort(estante) funciona porque Libro es Comparable: sin eso, tendríamos el ClassCastException de la teoría.
  • El más corto y el más largo salen gratis de las posiciones 0 y length - 1. Ordenar una vez es mucho más barato que buscar el mínimo y el máximo por separado.

Solución del Nivel 2

Jugador.java

public class Jugador {
    private final String nombre;
    private final int puntos;
    private final int partidos;

    public Jugador(String nombre, int puntos, int partidos) {
        this.nombre = nombre;
        this.puntos = puntos;
        this.partidos = partidos;
    }

    public String getNombre() { return nombre; }
    public int getPuntos() { return puntos; }
    public int getPartidos() { return partidos; }

    @Override
    public String toString() {
        return nombre + " " + puntos + " pts en " + partidos + " partidos";
    }
}

N2.java

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class N2 {
    // Más puntos primero; si empatan, menos partidos jugados; si siguen empatando, por nombre
    static final Comparator<Jugador> CLASIFICACION = Comparator
            .comparingInt(Jugador::getPuntos).reversed()
            .thenComparingInt(Jugador::getPartidos)
            .thenComparing(Jugador::getNombre);

    public static void main(String[] args) {
        List<Jugador> plantilla = new ArrayList<>(List.of(
            new Jugador("Noa", 120, 10), new Jugador("Iker", 150, 12),
            new Jugador("Lía", 150, 10), new Jugador("Hugo", 90, 8),
            new Jugador("Aitana", 120, 10)));

        plantilla.sort(CLASIFICACION);

        System.out.println("Top 3:");
        for (int i = 0; i < 3; i++) {
            System.out.println("  " + (i + 1) + ". " + plantilla.get(i));
        }
        System.out.println("Último: " + plantilla.get(plantilla.size() - 1));
    }
}

Por qué está hecho así:

  • Jugador no es Comparable: una clasificación es un criterio concreto de una liga, no «el» orden de un jugador. Por eso vive en un Comparator.
  • comparingInt(Jugador::getPuntos).reversed() invierte solo los puntos. Los partidos se añaden después con thenComparingInt, así que van de menos a más.
  • Lía e Iker empatan a 150 puntos; Lía va primero por jugar menos partidos (10 frente a 12). Noa y Aitana empatan a 120 con los mismos partidos, y gana el nombre: Aitana antes que Noa… pero en el top 3 solo entra Aitana. Cambia los datos y comprueba que el desempate final funciona.
  • La constante está en static final: un comparador no tiene estado, se puede reutilizar y compartir sin riesgo.

Solución del Nivel 3

Estadio.java

public class Estadio implements Cloneable {
    private String nombre;
    private int aforo;

    public Estadio(String nombre, int aforo) {
        this.nombre = nombre;
        this.aforo = aforo;
    }

    public void setAforo(int aforo) { this.aforo = aforo; }

    @Override
    public Estadio clone() {
        try {
            return (Estadio) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public String toString() { return nombre + " (" + aforo + ")"; }
}

Jugador.java

public class Jugador implements Cloneable {
    private final String nombre;
    private int puntos;

    public Jugador(String nombre, int puntos) {
        this.nombre = nombre;
        this.puntos = puntos;
    }

    public void sumar(int p) { puntos += p; }

    @Override
    public Jugador clone() {
        try {
            return (Jugador) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public String toString() { return nombre + ":" + puntos; }
}

Plantilla.java

import java.util.ArrayList;
import java.util.List;

public class Plantilla implements Cloneable {
    private String equipo;
    private List<Jugador> jugadores = new ArrayList<>();
    private Estadio estadio;

    public Plantilla(String equipo, Estadio estadio) {
        this.equipo = equipo;
        this.estadio = estadio;
    }

    public void fichar(Jugador j) { jugadores.add(j); }
    public List<Jugador> getJugadores() { return jugadores; }
    public Estadio getEstadio() { return estadio; }

    @Override
    public Plantilla clone() {
        try {
            Plantilla copia = (Plantilla) super.clone();
            // La lista nueva debe contener COPIAS de los jugadores, no los mismos objetos
            copia.jugadores = new ArrayList<>();
            for (Jugador j : this.jugadores) {
                copia.jugadores.add(j.clone());
            }
            copia.estadio = this.estadio.clone();
            return copia;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public String toString() {
        return equipo + " " + jugadores + " en " + estadio;
    }
}

N3.java

public class N3 {
    public static void main(String[] args) {
        Plantilla original = new Plantilla("Tigres", new Estadio("La Isla", 5000));
        original.fichar(new Jugador("Noa", 10));
        original.fichar(new Jugador("Iker", 12));

        Plantilla copia = original.clone();
        copia.fichar(new Jugador("Lía", 7));
        copia.getJugadores().get(0).sumar(100);
        copia.getEstadio().setAforo(9000);

        System.out.println("Original: " + original);
        System.out.println("Copia:    " + copia);
        System.out.println("¿Mismo jugador en la posición 0? "
                + (original.getJugadores().get(0) == copia.getJugadores().get(0)));
    }
}

Por qué está hecho así:

  • Estadio y Jugador solo tienen primitivos y String, así que su clone() es el más sencillo: super.clone() y nada más.
  • En Plantilla.clone(), super.clone() copia la referencia a la lista y al estadio. Por eso las reemplazamos por una lista nueva y un estadio clonado. Si solo hubiéramos copiado la lista con new ArrayList<>(this.jugadores), la lista sería nueva pero los jugadores seguirían siendo los mismos objetos.
  • La última línea de la salida lo demuestra: false significa que el primer jugador de la copia es otro objeto. Por eso sumarle 100 puntos no ha tocado a Noa en la original.
  • El try/catch está dentro de cada clone() para que quien llame no tenga que manejar la excepción comprobada. Es la receta de la teoría.

Míralo paso a paso en Python Tutor

El fallo de la resta es un buen candidato para verlo en vivo, porque ocurre dentro de una sola línea. Entra en pythontutor.com/java.html, elige Java y pega este programa, que compara los mismos dos números de las dos maneras:

public class Tutor {
    static int compararMal(int a, int b) {
        return a - b;
    }

    static int compararBien(int a, int b) {
        return Integer.compare(a, b);
    }

    public static void main(String[] args) {
        int grande = 2_000_000_000;
        int pequeno = -2_000_000_000;
        int mal = compararMal(grande, pequeno);
        int bien = compararBien(grande, pequeno);
        System.out.println("a - b = " + mal);
        System.out.println("Integer.compare = " + bien);
    }
}
a - b = -294967296
Integer.compare = 1

Qué observar, paso a paso:

  1. Al empezar main, fíjate en grande (2 000 000 000) y pequeno (-2 000 000 000): ambos caben perfectamente en un int.
  2. Entra en compararMal(a, b). Antes de ejecutar el return, calcula tú la resta: debería ser 4 000 000 000, un número positivo.
  3. Al volver a main, mira el valor de mal: es negativo (-294 967 296). El int se ha desbordado y Java no ha dado ningún aviso.
  4. Después, compararBien devuelve 1, que es el resultado correcto: grande va después de pequeno. Compara los dos signos y entenderás el fallo del fragmento 1.

Chuletario: Comparable y Cloneable

Lo esencial en 30 segundos

  • **Comparable<T>**: compareTo define el orden natural de la clase. Negativo = antes, 0 = equivalentes, positivo = después.
  • **Comparator<T>: criterio externo**; puedes tener todos los que quieras y encadenarlos.
  • Nunca restes para comparar: usa Integer.compare, Double.compare o compareTo.
  • Un orden que da 0 para elementos distintos pierde datos en TreeSet y TreeMap.
  • **clone()**: implements Cloneable + public + super.clone() + try/catch.
  • super.clone() es superficial: lo mutable (listas, arrays, otros objetos) hay que duplicarlo a mano.

Sintaxis

Quiero…CódigoResultado
Orden natural de mi claseclass A implements Comparable<A> { public int compareTo(A o) {…} }sort(lista) funciona
Comparar enterosInteger.compare(this.x, o.x)Sin desbordamiento
Comparar con desempateif (c != 0) return c; return this.n.compareTo(o.n);Nunca 0 para distintos
Ordenar con criterio externolista.sort(comparador)No hace falta Comparable
Criterio por un campoComparator.comparing(A::getN)Orden ascendente
InvertirComparator.comparingInt(A::getX).reversed()Descendente
Encadenar desempates….thenComparing(A::getN)Segundo criterio
Orden natural con null al finalComparator.nullsLast(Comparator.naturalOrder())Sin NullPointerException
Conjunto ordenadonew TreeSet<>(comparador)Solo mira el comparador
Copiar un objetoA copia = original.clone();Necesita public clone()
Copia profundacopia.lista = new ArrayList<>(this.lista);Lista propia

Plantillas de los ejercicios

**compareTo sin restas** (Nivel 1):

@Override
public int compareTo(Libro otro) {
    return Integer.compare(this.paginas, otro.paginas);
}

Comparator encadenado (Nivel 2):

static final Comparator<Jugador> CLASIFICACION = Comparator
        .comparingInt(Jugador::getPuntos).reversed()   // solo invierte los puntos
        .thenComparingInt(Jugador::getPartidos)
        .thenComparing(Jugador::getNombre);

**clone() profundo** (Nivel 3):

@Override
public Plantilla clone() {
    try {
        Plantilla copia = (Plantilla) super.clone();
        copia.jugadores = new ArrayList<>();
        for (Jugador j : this.jugadores) {
            copia.jugadores.add(j.clone());   // cada jugador, también copiado
        }
        copia.estadio = this.estadio.clone();
        return copia;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError(e);
    }
}

Errores que más se repiten

SíntomaCausa probable
ClassCastException … cannot be cast to class java.lang.ComparableLa clase no implementa Comparable y la ordenas sin Comparator
«is not abstract and does not override abstract method compareTo»implements Comparable sin escribir compareTo
Orden raro, sin excepción, con valores muy grandescompareTo con una resta que se desborda
Faltan elementos en un TreeSet/TreeMapEl criterio da 0 para elementos que consideras distintos
NullPointerException dentro de sortHay un null en la lista o el array
CloneNotSupportedExceptionFalta implements Cloneable
«clone() has protected access in Object»Falta redefinir clone() como public
La copia cambia al originalCopia superficial: lista u objeto compartidos
ClassCastException en clone() de una subclaseLa superclase hace new en vez de super.clone()

Antes de entregar, pregúntate

  1. ¿Mi compareTo usa **Integer.compare/Double.compare/compareTo** y no una resta?
  2. ¿Mi criterio de orden distingue a los elementos que son distintos (hay desempate)?
  3. ¿Mi clone() empieza con **super.clone()** y nunca con new?
  4. Si hay listas, arrays u objetos mutables dentro, ¿los he duplicado en la copia?

Si quieres repasar el tema desde la base, vuelve a la teoría de Comparable y Cloneable.

Sigue aprendiendoTodos los artículos de Java

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *