2

I'm trying to create a class that can either be compared to an instance of the same class, or to String.

For example, consider the following:

public class Record implements Comparable<Record> {
    public String name;

    public Record(String name) {
        this.name = name;
    }

    public int compareTo(Record o) {
        return name.compareTo(o.name);
    }
}

I then place this into an ArrayList as follows:

ArrayList<Record> records = new ArrayList<Record>();
records.add(new Record("3"));
records.add(new Record("1"));
records.add(new Record("2"));

If I then sort them, they are sorted properly:

Collections.sort(records);

However, I not want to be able to get a record via binary search based on a string. For example:

int index = Collections.binarySearch(records, "3");

The problem is that there is no compareTo method that takes a String as argument, and I'm unsure how to implement it.

I tried doing:

public class Record implements Comparable<Record>, Comparable<String> {
    public String name;

    public Record(String name) {
        this.name = name;
    }

    public int compareTo(Record o) {
        return name.compareTo(o.name);
    }

    public int compareTo(String o) {
        return name.compareTo(o);
    }
}

But, of course, you cannot implement the same interface with different arguments more than once.

So, I'm looking for a method to do the above. I have looked at the following previous answers, but have not found one that truly answers this sufficiently. At least, if it did, I did not understand it.

Basically, what I want to do is the following:

public int compare(Record r, String s) {
    return r.name.compareTo(s);
}

As far as I can tell, though, you cannot compare objects of differing types without implementing some sort of common interface or superclass. I don't really have that option with String.

Can someone please show me how to do this, if it is possible? Thanks.

UPDATE I realize that I could do the following:

Collections.binarySearch(records, new Record("3"));

But, that is not what I'm after. Thanks.

Community
  • 1
  • 1
crush
  • 16,713
  • 9
  • 59
  • 100

4 Answers4

4

You can implement Comparable without qualifying it with a type, and check the incoming Object with instanceof, and behave differently depending on what type you receive.

Ian McMahon
  • 1,660
  • 11
  • 13
1

You cannot do that. from javadoc

It follows immediately from the contract for compareTo that the quotient is an equivalence relation on C and String's compareTo will never return true for your Record.

What you could do, is create Comparator that would compare Strings and Records. Just leave it without generics.

Alpedar
  • 1,314
  • 1
  • 8
  • 12
  • also from javadoc: `It is strongly recommended (though not required) that natural orderings be consistent with equals.` – Ian McMahon Feb 25 '13 at 20:03
1

You can try the next for getting the index in the sorted collection:

class Record implements Comparable<String> {

    public String name;

    public Record(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(String o) {
        return name.compareTo(o);
    }

}

And:

public static void main(String[] args) {

    ArrayList<Record> records = new ArrayList<Record>();
    records.add(new Record("3"));
    records.add(new Record("1"));
    records.add(new Record("2"));

    Collections.sort(records, new Comparator<Record>() {
        @Override
        public int compare(Record a, Record b) {
            return a.name.compareTo(b.name);
        }
    });

    int index = Collections.binarySearch(records, "3");
    System.out.println(index); // 2

}
Paul Vargas
  • 41,222
  • 15
  • 102
  • 148
  • So instead of making it `Comparable`, I make it only `Comparable` without defining a custom `Comparator`. It's not a bad idea. I could take it even further by encapsulating the `new Comparator() {}` in a `public Comparator getComparator() {}` method in my Record class, I suppose. – crush Feb 25 '13 at 20:12
0

Is there some problem with defining the interface ComparableToString and implementing it? Its compareTo method can restrict callers to String. No instanceof, no casting required.

    public class Record implements Comparable<Record>, ComparableToString
    {
        public String name;

        public Record(String name)
        {
            this.name = name;
        }

        public int compareTo(Record o)
        {
            return name.compareTo(o.name);
        }

        public int compareTo(String o)
        {
            return name.compareTo(o);
        }
    }
    interface ComparableToString
    {
        public int compareTo(String s);
    }
arcy
  • 12,845
  • 12
  • 58
  • 103
  • Can you provide an example of what you mean? – crush Feb 25 '13 at 19:47
  • `Collections.binarySearch(records, "3");` does not work with this example. I've tried it already. I get the error: `The method binarySearch(List extends Comparable super T>>, T) in the type Collections is not applicable for the arguments (ArrayList, String)` – crush Feb 25 '13 at 19:52
  • Yes, this won't work for your *extended* question -- for your original "I'm trying to create a class that can either be compared to an instance of the same class, or to String.", it does ok. But you also want to feed the resulting class to something that is going to use the Comparable interface, and this won't handle that. – arcy Feb 25 '13 at 20:05