CodeGym /Courses /JAVA 25 SELF /Interfaces from the standard library: Comparable, Seriali...

Interfaces from the standard library: Comparable, Serializable, etc.

JAVA 25 SELF
Level 21 , Lesson 4
Available

1. Interface Comparable<T>

Have you ever sorted a list of numbers or strings? Of course! Now imagine you have a list of your own objects — for example, students, products, or cats. How will Java know in what order to sort them? That’s exactly what the Comparable<T> interface is for.

This interface defines the “natural order” of objects — the order that makes sense for a given data type. For numbers — ascending; for strings — alphabetical; for students — by last name or by age (your choice).

How Comparable works

The interface is very simple: it has only one method:

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

The compareTo method should return:

  • a negative number if the current object is “less than” the other;
  • 0 if they are “equal”;
  • a positive number if it is “greater”.

Example: sorting students by age

Let’s add a Student class and implement the Comparable<Student> interface for it:

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 for the example
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public int compareTo(Student other) {
        // Sort by age (ascending)
        return Integer.compare(this.age, other.age);
    }

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

Now we can easily sort an array or a list of students:

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); // Works thanks to Comparable!

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

Result:

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

Important nuance

If you implement Comparable, try to keep compareTo consistent with equals. That is, if a.compareTo(b) == 0, then a.equals(b) should be true. Otherwise, sorting and collections may behave unpredictably — and you’ll have grounds for philosophical musings about the meaning of a programmer’s life.

2. Interface Serializable

Serialization is an object’s ability to turn into a sequence of bytes (for example, to save itself to a file or send over the network) and then be restored back. Imagine you want to save your game state or send an object to a server — you can’t do it without serialization.

In Java there is a marker interface called Serializable for this. Marker means it doesn’t contain any methods; it simply “marks” a class as serializable.

import java.io.Serializable;

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

    // ... the rest of the code
}

How to serialize an object

For serialization and deserialization, use ObjectOutputStream and ObjectInputStream. Example — save an object to a file and read it back:

import java.io.*;

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

        // Save the object to a file
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student.dat"))) {
            out.writeObject(s);
        }

        // Read the object from the file
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.dat"))) {
            Student loaded = (Student) in.readObject();
            System.out.println("Loaded: " + loaded);
        }
    }
}

Note: All fields of the object (and nested objects) must also be serializable, otherwise you will get an error.

Why the marker interface is needed

The Serializable interface doesn’t require you to implement methods — it simply tells the JVM: “this object can be serialized.” If you forget to implement it, an attempt to serialize will result in a NotSerializableException.

3. Other important interfaces of the standard library

Interface Cloneable

Another marker interface. Its job is to let the JVM know that an object can be cloned using Object.clone(). Without it, an attempt to call clone() will throw an exception.

However, cloning in Java is tricky. The default is a shallow copy, and often it’s better to write your own copying methods instead.

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

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

Interface AutoCloseable

This interface contains just one method, close(). Any class that implements it can be used in a try-with-resources construct — automatic closing of resources (e.g., files, streams):

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

public class Main {
    public static void main(String[] args) {
        try (MyResource res = new MyResource()) {
            System.out.println("Working with the resource");
        }
        // Here res.close() will be called automatically
    }
}

Interface Iterable<T>

This interface allows your object to be “iterable” in a for-each loop. It contains only one method, iterator(), which returns an Iterator<T>.

public class MyList implements Iterable<String> {
    // ... internal storage

    @Override
    public java.util.Iterator<String> iterator() {
        // Return an iterator to traverse the elements
        return ...;
    }
}

All standard collections (ArrayList, HashSet, etc.) implement Iterable, so they can be iterated with for-each.

Interface Comparator<T>

This interface lets you compare objects by different rules without changing the objects themselves. For example, sort students by name instead of by age.

import java.util.Comparator;

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

In modern Java this is usually done with lambda expressions:

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

Observer, EventListener

These interfaces are used to implement the “observer” and “event listener” patterns — when one object reacts to events occurring in another. For example, in graphical user interfaces (Swing, JavaFX) button handlers implement the ActionListener interface.

4. Practice: implement Comparable and serialize an object

Example 1. Comparable for a custom class

Let’s write a Book class that can be sorted by year of publication:

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 For Dummies", 2018),
            new Book("War and Peace", 1869),
            new Book("Harry Potter", 1997)
        );
        Collections.sort(books);
        System.out.println(books);
    }
}

Result:

[War and Peace (1869), Harry Potter (1997), Java For Dummies (2018)]

Example 2. Serializing an object

import java.io.*;

public class Book implements Serializable {
    private String title;
    private int year;
    // ... constructor, 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 For Dummies", 2018);

        // Save the object to a file
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("book.dat"))) {
            out.writeObject(book);
        }

        // Read the object from the file
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("book.dat"))) {
            Book loaded = (Book) in.readObject();
            System.out.println("Loaded: " + loaded);
        }
    }
}

5. Table: major interfaces of the standard library

Interface Purpose Key methods Example usage
Comparable<T>
Natural ordering of objects
int compareTo(T o)
Sorting lists
Comparator<T>
Custom object comparison
int compare(T a, T b)
Sorting by different rules
Serializable
Object serialization — (marker) Saving/loading objects
Cloneable
Object cloning — (marker) Creating object copies
AutoCloseable
Automatic resource closing
void close()
try-with-resources
Iterable<T>
Iterating over elements in collections
Iterator<T> iterator()
for-each loop
Observer / EventListener Reacting to events
update(), actionPerformed
Event handling in UI, patterns

6. Common mistakes when working with standard interfaces

Error No. 1: The interface isn’t implemented, but the functionality is required.
For example, you forgot to implement Serializable but try to serialize an object — you’ll get a NotSerializableException. Similarly with Cloneable and calling clone().

Error No. 2: Violating the Comparable and equals contract.
If a.compareTo(b) == 0 but a.equals(b) does not hold, collections may behave strangely. For example, TreeSet may “lose” objects.

Error No. 3: Shallow copying when cloning.
By default, clone() copies only the “top layer” of an object. If you have fields that reference other objects, they won’t be deeply copied. This can lead to puzzling bugs.

Error No. 4: Not using try-with-resources.
If a class implements AutoCloseable but you don’t use it in try-with-resources, you risk forgetting to close the resource — leading to a memory leak or a file lock.

Error No. 5: Incorrect implementation of compareTo or compare.
If you return only 0 or 1, instead of a negative/zero/positive number, sorting will work incorrectly.

1
Task
JAVA 25 SELF, level 21, lesson 4
Locked
Comparison of numeric "containers" 📦
Comparison of numeric "containers" 📦
1
Task
JAVA 25 SELF, level 21, lesson 4
Locked
Sorting cities by population 🏙️
Sorting cities by population 🏙️
1
Task
JAVA 25 SELF, level 21, lesson 4
Locked
Alternative product sorting in the store 🏷️
Alternative product sorting in the store 🏷️
1
Task
JAVA 25 SELF, level 21, lesson 4
Locked
Automatic secure resource closing 🔑
Automatic secure resource closing 🔑
1
Survey/quiz
Advanced interfaces, level 21, lesson 4
Unavailable
Advanced interfaces
Advanced interfaces and functional interfaces
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION