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.

9
  • 1
    In Java, you always have a common superclass, Object. Commented Feb 25, 2013 at 19:41
  • @PatriciaShanahan If I use Comparable<Object>, wouldn't that allow comparisons with objects other than String or Record, though? Commented Feb 25, 2013 at 19:42
  • Is int included int that always list? Commented Feb 25, 2013 at 19:44
  • 2
    @crush You can throw ClassCastException at run time if you don't like the type. Commented Feb 25, 2013 at 19:45
  • @skuntsel I meant in the context of things that could appear as the generic type for Comparable. Commented Feb 25, 2013 at 19:46

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

And if it's not an expected type, should I throw an exception?
Yes, as per the Comparable API, throw a ClassCastException if it's not an expected type
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.

1 Comment

also from javadoc: It is strongly recommended (though not required) that natural orderings be consistent with equals.
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

}

1 Comment

So instead of making it Comparable<Record>, I make it only Comparable<String> without defining a custom Comparator. It's not a bad idea. I could take it even further by encapsulating the new Comparator<Record>() {} in a public Comparator<Record> getComparator() {} method in my Record class, I suppose.
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);
    }

3 Comments

Can you provide an example of what you mean?
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<Record>, String)
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.