Skip to main content

Comparators Should Be "Serializable"

High
maintainabilityreliabilitydefensive_programming

What is it?

A Comparator is an interface used for sorting or ordering collections. By default, it is not serializable and can prevent an otherwise-Serializable ordered collection from being serialized.

Why apply it?

Making a Comparator serializable ensures that collections using these comparators remain serializable. The change allows for good defensive programming and often requires minimal overhead.

How to fix it?

To fix this issue, ensure that Comparator implementations also implement Serializable.

Examples

Example 1:

Negative

In this negative example, the comparator is not serializable, which can cause serialization issues with collections using it.

import java.util.Comparator;

public class FruitComparator implements Comparator<Fruit> { // Noncompliant
@Override
public int compare(Fruit f1, Fruit f2) {
return f1.getName().compareTo(f2.getName());
}

@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof FruitComparator;
}
}

Example 2:

Positive

This positive example shows how to declare a comparator that is serializable, maintaining serializable integrity for collections using it.

import java.io.Serializable;
import java.util.Comparator;

public class FruitComparator implements Comparator<Fruit>, Serializable {
private static final long serialVersionUID = 1L;

@Override
public int compare(Fruit f1, Fruit f2) {
return f1.getName().compareTo(f2.getName()); // Compliant
}

@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof FruitComparator; // Compliant
}
}

Negative

This negative example does not implement Serializable, which may prevent serialization when used within a serializable collection.

import java.util.Comparator;
import java.util.Locale;

public class LexicalFruitComparator implements Comparator<Fruit> { // Noncompliant
@Override
public int compare(Fruit f1, Fruit f2) {
return f1.getName().toLowerCase(Locale.ROOT).compareTo(f2.getName().toLowerCase(Locale.ROOT));
}

@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof LexicalFruitComparator;
}
}

Example 3:

Positive

Another positive example demonstrating a lexical comparator that implements Serializable, ensuring compatibility with serializable collections.

import java.io.Serializable;
import java.util.Comparator;
import java.util.Locale;

public class LexicalFruitComparator implements Comparator<Fruit>, Serializable {
private static final long serialVersionUID = 2L;

@Override
public int compare(Fruit f1, Fruit f2) {
return f1.getName().toLowerCase(Locale.ROOT).compareTo(f2.getName().toLowerCase(Locale.ROOT)); // Compliant
}

@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof LexicalFruitComparator; // Compliant
}
}