Comparing a string that can hold a numeric value I am refactoring a part of big old system developed in Java. I came across a situation where we are migrating some data types, and there will be use of both types. For compatibility, as all the old part will remain using id as an int, I simply overloaded the constructor, and the old getter now parse a integer value.

The thing is, how is the proper way to compare this data? Does this way make any sense, or is there a simpler way to compare it, considering that id can be both, a number or a word.

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;

public class SomeItem implements Comparable&lt;SomeItem&gt; {
    private String id;    

    public SomeItem(String id) {
        this.id = id;
    }

    public SomeItem(int no) {
        this(String.valueOf(no));
    }

    public String getId() {
        return id;
    }

    public int getNo() {
        return Integer.parseInt(id);
    }

    @Override
    public int compareTo(SomeItem that) {
        if (StringUtils.isNumeric(this.getId()) &amp;&amp; StringUtils.isNumeric(that.getId())) {
            return this.getNo() - that.getNo();
        } else {
            return this.getId().compareTo(that.getId());
        }
    }
}
this.getNo() - that.getNo()


may violate the contract of compareTo() if the subtraction overflows or underflows which is a likely event if the compared values are integer IDs of a large set of elements.

Instead you should explicitly compare the two:

return Integer.compare(this.getNo(), that.getNo());


Another way to avoid numerical comparison altogether is to format numbers as fixed-width strings with a DecimalFormat and compare the resulting strings (including some example code at the end):

import java.text.DecimalFormat;

public class SomeItem implements Comparable&lt;SomeItem&gt; {

    private static final DecimalFormat fmt = new DecimalFormat();

    static {
        int len = 19; // = String.valueOf(Long.MAX_VALUE).length();
        fmt.setMinimumIntegerDigits(len);
        fmt.setMaximumIntegerDigits(len);
        fmt.setPositivePrefix("+");
        fmt.setGroupingUsed(false);
    }

    private String id;

    public SomeItem(String id) {
        this.id = id;
    }

    public SomeItem(int no) {
        this(fmt.format(no));
    }

    public String getId() {
        return id;
    }

    public int getNo() {
        return Integer.parseInt(id);
    }

    @Override
    public int compareTo(SomeItem that) {
        return this.getId().compareTo(that.getId());
    }

    public static void main(String[] args)
    {
        java.io.PrintStream o = System.out;

        SomeItem[] items = new SomeItem[] {
            new SomeItem(1),
            new SomeItem(-1),
            new SomeItem(Integer.MAX_VALUE),
            new SomeItem(Integer.MIN_VALUE)
        };

        for (SomeItem i: items)
            System.out.format("id=%s, no=%+020d%n", i.getId(), i.getNo());
    }
}


Additionally, I based the amount of digits on long instead of int to provide room for (future) growth. I would have replaced the latter by the former entirely if Java converted the return value of #getNo() silently from long to int.