CodeGym /Cursos /JAVA 25 SELF /Interfaces da biblioteca padrão: Comparable, Serializable...

Interfaces da biblioteca padrão: Comparable, Serializable e outros.

JAVA 25 SELF
Nível 21 , Lição 4
Disponível

1. Interface Comparable<T>

Você já ordenou uma lista de números ou strings? Claro que sim! Agora imagine que você tem uma lista de seus próprios objetos — por exemplo, estudantes, produtos ou gatinhos. Como o Java vai entender em que ordem ordená-los? É exatamente para isso que serve a interface Comparable<T>.

Essa interface define a “ordem natural” dos objetos — ou seja, a ordem que faz sentido para esse tipo de dado. Por exemplo, para números — crescente; para strings — alfabética; para estudantes — por sobrenome ou idade (você escolhe).

Como o Comparable funciona

A interface é muito simples: ela tem apenas um método:

public interface Comparable<T> {
    int compareTo(T o);
}

O método compareTo deve retornar:

  • um número negativo, se o objeto atual for “menor” que o outro;
  • 0, se forem “iguais”;
  • um número positivo, se for “maior”.

Exemplo: ordenando estudantes por idade

Vamos adicionar a classe Student e implementar para ela a interface Comparable<Student>:

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters para o exemplo
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public int compareTo(Student other) {
        // Ordenamos por idade (crescente)
        return Integer.compare(this.age, other.age);
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

Agora podemos ordenar facilmente um array ou uma lista de estudantes:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Vasya", 20));
        students.add(new Student("Petya", 18));
        students.add(new Student("Masha", 22));

        Collections.sort(students); // Funciona graças ao Comparable!

        System.out.println("Estudantes ordenados:");
        for (Student s : students) {
            System.out.println(s);
        }
    }
}

Resultado:

Petya (18)
Vasya (20)
Masha (22)

Uma nuance importante

Se você implementar Comparable, procure manter o compareTo consistente com o equals. Ou seja, se a.compareTo(b) == 0, então a.equals(b) deve ser true. Caso contrário, a ordenação e as coleções podem se comportar de forma imprevisível — e você ganhará um motivo para reflexões filosóficas sobre o sentido da vida do programador.

2. Interface Serializable

Serialização é a capacidade de um objeto se transformar em uma sequência de bytes (por exemplo, para se salvar em arquivo ou enviar pela rede) e depois ser restaurado. Imagine que você quer salvar o estado do seu jogo ou enviar um objeto para o servidor — sem serialização não dá.

No Java existe a interface marcador Serializable para isso. “Marcador” significa que ela não contém métodos — apenas “marca” a classe como serializável.

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // ... restante do código
}

Como serializar um objeto

Para serialização e desserialização usam-se as classes ObjectOutputStream e ObjectInputStream. Exemplo — salvamos um objeto em arquivo e lemos de volta:

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        Student s = new Student("Katya", 19);

        // Salvamos o objeto em arquivo
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student.dat"))) {
            out.writeObject(s);
        }

        // Lemos o objeto do arquivo
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.dat"))) {
            Student loaded = (Student) in.readObject();
            System.out.println("Carregado: " + loaded);
        }
    }
}

Observação: Todos os campos do objeto (e dos objetos aninhados) também devem ser serializáveis; caso contrário, ocorrerá um erro.

Por que precisamos de uma interface marcador

A interface Serializable não exige a implementação de métodos — ela apenas informa à JVM: “este objeto pode ser serializado”. Se você esquecer de implementá-la, a tentativa de serialização resultará na exceção NotSerializableException.

3. Outras interfaces importantes da biblioteca padrão

Interface Cloneable

Mais uma interface marcador. Sua função é deixar claro para a JVM que o objeto pode ser clonado usando o método Object.clone(). Sem ela, a tentativa de chamar clone() lançará uma exceção.

No entanto, clonagem em Java é um tema traiçoeiro. A clonagem padrão é superficial (shallow copy) e, muitas vezes, é melhor escrever métodos próprios de cópia.

public class Student implements Cloneable {
    private String name;
    private int age;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Interface AutoCloseable

Essa interface contém apenas um método close(). Qualquer classe que a implemente pode ser usada com a construção try-with-resources — fechamento automático de recursos (por exemplo, arquivos, streams):

public class MyResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Recurso fechado!");
    }
}

public class Main {
    public static void main(String[] args) {
        try (MyResource res = new MyResource()) {
            System.out.println("Trabalhando com o recurso");
        }
        // Aqui res.close() será chamado automaticamente
    }
}

Interface Iterable<T>

Essa interface permite que seu objeto seja “iterável” em um laço for-each. Ela contém apenas um método iterator(), que retorna um objeto Iterator<T>.

public class MyList implements Iterable<String> {
    // ... armazenamento interno

    @Override
    public java.util.Iterator<String> iterator() {
        // Retornamos um iterador para percorrer os elementos
        return ...;
    }
}

Todas as coleções padrão (ArrayList, HashSet etc.) implementam Iterable, por isso podem ser percorridas com for-each.

Interface Comparator<T>

Essa interface permite comparar objetos por diferentes regras, sem alterar os próprios objetos. Por exemplo, ordenar estudantes pelo nome, e não pela idade.

import java.util.Comparator;

Comparator<Student> byName = new Comparator<Student>() {
    @Override
    public int compare(Student a, Student b) {
        return a.getName().compareTo(b.getName());
    }
};

Na Java moderna, isso normalmente é feito com expressões lambda:

Comparator<Student> byName = (a, b) -> a.getName().compareTo(b.getName());

Observer, EventListener

Essas interfaces são usadas para implementar os padrões “observador” e “ouvinte de eventos” — quando um objeto reage a eventos que ocorrem em outro. Por exemplo, em interfaces gráficas (Swing, JavaFX) os handlers de botões implementam a interface ActionListener.

4. Prática: implementando Comparable e serializando um objeto

Exemplo 1. Comparable para sua própria classe

Vamos escrever a classe Book, que pode ser ordenada pelo ano de publicação:

public class Book implements Comparable<Book> {
    private String title;
    private int year;

    public Book(String title, int year) {
        this.title = title;
        this.year = year;
    }

    @Override
    public int compareTo(Book other) {
        return Integer.compare(this.year, other.year);
    }

    @Override
    public String toString() {
        return title + " (" + year + ")";
    }
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Book> books = Arrays.asList(
            new Book("Java Para Leigos", 2018),
            new Book("Guerra e Paz", 1869),
            new Book("Harry Potter", 1997)
        );
        Collections.sort(books);
        System.out.println(books);
    }
}

Resultado:

[Guerra e Paz (1869), Harry Potter (1997), Java Para Leigos (2018)]

Exemplo 2. Serialização de um objeto

import java.io.*;

public class Book implements Serializable {
    private String title;
    private int year;
    // ... construtor, getters, toString

    public Book(String title, int year) {
        this.title = title;
        this.year = year;
    }

    @Override
    public String toString() {
        return title + " (" + year + ")";
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Book book = new Book("Java Para Leigos", 2018);

        // Salvamos o objeto em arquivo
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("book.dat"))) {
            out.writeObject(book);
        }

        // Lemos o objeto do arquivo
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("book.dat"))) {
            Book loaded = (Book) in.readObject();
            System.out.println("Carregado: " + loaded);
        }
    }
}

5. Tabela: principais interfaces da biblioteca padrão

Interface Finalidade Métodos-chave Exemplo de uso
Comparable<T>
Ordem natural dos objetos
int compareTo(T o)
Ordenação de listas
Comparator<T>
Comparação personalizada de objetos
int compare(T a, T b)
Ordenação por regras diferentes
Serializable
Serialização de objetos — (marcador) Salvar/carregar objetos
Cloneable
Clonagem de objetos — (marcador) Criação de cópias de objetos
AutoCloseable
Fechamento automático de recursos
void close()
try-with-resources
Iterable<T>
Iteração de elementos em coleções
Iterator<T> iterator()
loop for-each
Observer / EventListener Reação a eventos
update(), actionPerformed
Tratamento de eventos em UI, padrões

6. Erros comuns ao trabalhar com as interfaces padrão

Erro nº 1: A interface não foi implementada, mas o recurso é necessário.
Por exemplo, você esqueceu de implementar Serializable e tenta serializar um objeto — terá NotSerializableException. O mesmo vale para Cloneable e a chamada de clone().

Erro nº 2: Violação do contrato de Comparable e equals.
Se a.compareTo(b) == 0, mas não for verdadeiro que a.equals(b), as coleções podem se comportar de forma estranha. Por exemplo, o TreeSet pode “perder” objetos.

Erro nº 3: Cópia superficial ao clonar.
O método clone() por padrão copia apenas a “camada superior” do objeto. Se você tiver campos que referenciam outros objetos, eles não serão copiados profundamente. Isso pode levar a bugs enigmáticos.

Erro nº 4: Não utilizar try-with-resources.
Se a classe implementa AutoCloseable, mas você não a usa com try-with-resources, corre o risco de esquecer de fechar o recurso — e provocar um vazamento de memória ou o bloqueio de um arquivo.

Erro nº 5: Implementação incorreta de compareTo ou compare.
Se retornar apenas 0 ou 1, em vez de números negativo/zero/positivo, a ordenação funcionará incorretamente.

1
Pesquisa/teste
Interfaces avançadas, nível 21, lição 4
Indisponível
Interfaces avançadas
Interfaces avançadas e interfaces funcionais
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION