Comparable loses its interface powers.

R

Roedy Green

If Comparable were just an ordinary interface you could say things
like this:

Comparable a = "abc";
Comparable b = Double.valueOf ( 10.0d );
Comparable c = Double.valueOf ( 12.9d );


int x = b.compareTo( c );

But generics butt in and it seems to be impossible to keep around
arrays of Comparables for sorting columns.

One way, which I find ugly is to sort Objects instead of Comparables
then cast them to specific types before calling compareTo. That should
not be necessary. All the JVM should need to know at compile time is
that a reference is implements comparable.
 
L

Lew

Thomas said:
Well, you can. Try it; it compiles and runs.

The Java compiler grumps a bit because it uses runtime typing.
Comparable.compareTo() throws a ClassCastException on type mismatch;
generics are meant to let the compiler check such things, rather than
issuing a runtime exception at runtime. In your situation, you have to
"know" which instances of Comparable can be compared to each other and
which cannot.

Nevertheless, Java supports dynamic typing and the raw type is not
deprecated in any way. Generics are a handy tools, not a mandatory
regulation.

You can do it generically, too, if you write a helper class or method that
takes the type of the column you want to sort (see the original post), either
at compile time or with a Class<T> run-time type token. You get your type
safety at a cost of a slight increase in verbosity.

You can also declare
Comparable <String> a = "abc";
Comparable <Double> b = Double.valueOf ( 10.0d );
Comparable <Double> c = Double.valueOf ( 12.9d );
int x = b.compareTo( c );

Untried:
public static <T> int compare( Comparable <T> a, Comparable <T> b )
{
return a.compareTo( b );
}

I'm not even sure you can call that more verbose since you add a method but
subtract the variable declarations in the caller.
 
R

Roedy Green

In your situation, you have to
"know" which instances of Comparable can be compared to each other and
which cannot.

In the old days, so long as each column contained compatible things,
you could just use Comparable.compareTo and all would automagically
sort itself out. Today you get squawking if you do it because, for
example, String no longer implements Comparable, it implements
Comparable<String> which will not cast to Comparable or
Comparable<Object>
--
Roedy Green Canadian Mind Products
http://mindprod.com

The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair.
~ Douglas Adams (born: 1952-03-11 died: 2001-05-11 at age: 49)
 
M

markspace

Roedy said:
If Comparable were just an ordinary interface you could say things ....
But generics butt in and it seems to be impossible to keep around
arrays of Comparables for sorting columns.


The problem here is that the Comparable interface is designed for the
class itself to compare to other objects. If you were the one
implementing this method, how many classes would you want to compare it
to?

Using your example of comparing Strings and Double, let's look at String
method:

public int compareTo( Object o ) {
if( o instanceof String )
... normal string compare...
else if( o instanceof Double )
... compare String to Double...
else if( o instanceof Integer )
... how many of these do you want?


It just goes on and on. Frankly, I think your question lacks any sort
of critical thought. It's pretty obvious what would happen if one
actually tried to implement your idea. Not good things.

And of course, this code would have to be duplicated inside Double, and
Integer, and every other class that you wanted to compare to. That the
real issue for me. It's just a stinky idea.

One way, which I find ugly is to sort Objects instead of Comparables


Ugly is as ugly does, I suppose. Maybe don't use Object? A little
effort wouldn't hurt here.

Let's see, off the top of my head. We don't want to compare Objects, so
we do want to make our own type.

public interface MyColumnType {}

It's just a marker interface for now. So we want to compare Strings,
but we can't extend String. We'll have to use composition.
Fortuneately, there's a string-like interface, CharacterSequence that
will help

public class MyStringType implements CharacterSequence, MyColumnType
private final String value;
public MyStringType( String v ) { value = v; }
....
}

We can't extend Double either, but there is a common Number class we can
use:

public class MyNumericType extends Number implements MyColumnType
private final Number value;
public MyNumericType( Number n ) { value = n; }
...
}

Now we can use a Comparator to implement the actual comparison

public class MyColumnComparator implements Comparator<MyColumnType>
{
public void compare(MyColumnTye o1, MyColumnTye o2 ) {

.. implement here..

}
}

Now at least it looks like someone made some sort of effort. I don't
have an algorithm for comparing Strings and Numbers for you, but then I
don't know how you want to compare them. It's a critical detail you
conveniently left out. I don't think there is a standard way of
comparing them, do you? So I'll just leave the rest of it to you then.
 
M

markspace

Roedy said:
In the old days, so long as each column contained compatible things,
you could just use Comparable.compareTo and all would automagically
sort itself out. Today you get squawking if you do it because, for
example, String no longer implements Comparable, it implements
Comparable<String> which will not cast to Comparable or
Comparable<Object>


I'm pretty sure String will cast to Comparable<String>.
 
L

Lew

I'm pretty sure String will cast to Comparable<String>.

Of course it will. I don't know where the notion comes from that 'String'
does not implement 'Comparable<String>', when it says right in the Javadocs
that it does.

And the requirement that generics impose that you specify what ensures that a
column "contained compatible things" is the key. Roedy waves a magic wand
with that offhand phrase, but the whole point of generics is to make that a
compile-time check, instead of a run-time check in "the old days". "All" did
not "automagically sort itself out" because if you violated that "so long as"
condition via a bug or other means, you'd get a weird results. I mean, what
if you put an Float and a Double, say, into the same column in "the old days"?
What would happen?

With generics you'd know that at compile time and avoid an expensive,
embarrassing bug. That's the thing that makes the "new days" so much better.
 
M

Mike Schilling

markspace said:
I'm pretty sure String will cast to Comparable<String>.

At worst, you can always use the universal Comparator:

public class Comparer implements Comparator<Comparable <?>>
{
public int compare(Comparable<?> o1, Comparable<?> o2)
{
Comparable<Object> oo1 = (Comparable<Object>) o1;
return oo1.compareTo(o2);
}
}

This acts exactly like Java 1.4: it will compare any two objects, quite
possibly leading to a runtime exception.
 
J

John B. Matthews

Roedy Green said:
But generics butt in and it seems to be impossible to keep around
arrays of Comparables for sorting columns.

I came up with this scheme for a type-safe Comparator<Record>. It lets
the compiler catch a Field that isn't Comparable:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/** @author John B. Matthews */
public class ComparatorTest {

public static void main(String[] args) {
List<Record> list = new ArrayList<Record>(Arrays.asList(
new Record(99, "Xxx"),
new Record(56, "Yyy"),
new Record(42, "Zzz")));
print(list, Sort.ASCENDING, Field.I);
print(list, Sort.DESCENDING, Field.S);
}

private static void print(List<Record> list, Sort s, Field f) {
RecordComparator rc = new RecordComparator(s, f);
Collections.sort(list, rc);
for (Record r : list) {
System.out.println(r);
}
}
}

class Record {

private Integer i;
private String s;

public Record(Integer i, String s) {
this.i = i;
this.s = s;
}

@Override
public String toString() {
return i + " " + s;
}

public int compareTo(Field field, Record record) {
switch (field) {
case I: return this.i.compareTo(record.i);
case S: return this.s.compareTo(record.s);
default: throw new IllegalArgumentException(
"Unable to sort Records by " + field.getType());
}
}
}

enum Sort { ASCENDING, DESCENDING; }

enum Field {

I(Integer.class), S(String.class);

private Class type;

Field(Class<? extends Comparable> type) {
this.type = type;
}

public Class getType() {
return type;
}
}

class RecordComparator implements Comparator<Record> {

private Field field;
private Sort sort;

public RecordComparator(Sort sort, Field field) {
this.sort = sort;
this.field = field;
}

@Override
public final int compare(Record a, Record b) {
int result = a.compareTo(field, b);
if (sort == Sort.ASCENDING) return result;
else return -result;
}
}
 
D

Daniel Pitts

If Comparable were just an ordinary interface you could say things
like this:

Comparable a = "abc";
Comparable b = Double.valueOf ( 10.0d );
Comparable c = Double.valueOf ( 12.9d );


int x = b.compareTo( c );

But generics butt in and it seems to be impossible to keep around
arrays of Comparables for sorting columns.
Well, you wouldn't want to keep any kind of arrays around, use List<Foo>
or Collection<Foo> instead...

Now, if you have a generic sortable container, you would use
MyContainer<T extends Comparable<T>> as the declared type, so that you
One way, which I find ugly is to sort Objects instead of Comparables
then cast them to specific types before calling compareTo. That should
not be necessary. All the JVM should need to know at compile time is
that a reference is implements comparable.
Yes, but at compile time, wouldn't you prefer avoiding
ClassCastException from within compareTo?
 
L

Lew

Mike said:
At worst, you can always use the universal Comparator:

public class Comparer implements Comparator<Comparable <?>>
{
public int compare(Comparable<?> o1, Comparable<?> o2)
{
Comparable<Object> oo1 = (Comparable<Object>) o1;
return oo1.compareTo(o2);
}
}

This acts exactly like Java 1.4: it will compare any two objects, quite
possibly leading to a runtime exception.

Now these are the good old days!
 
R

Roedy Green

String no longer implements Comparable, it implements


Of course it will. I don't know where the notion comes from that 'String'
does not implement 'Comparable<String>', when it says right in the Javadocs
that it does.

String DOES implement Comparable<String>. Nobody claimed it did not.
The problem is when you want to use the Comparable the way you did
before generics, where you compare any two Objects and the JVM sorted
out which compareTo method to use depending on the type of the Object.

You can't cast a Comparable<String> to a Comparable<Object>

--
Roedy Green Canadian Mind Products
http://mindprod.com

The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair.
~ Douglas Adams (born: 1952-03-11 died: 2001-05-11 at age: 49)
 
R

Roedy Green

I mean, what
if you put an Float and a Double, say, into the same column in "the old days"?
What would happen?

A Comparable in the old days had an Object parameter, with no generic
narrowing guarantee. Often code would check that the object was of
the desired type before the compare.

If you look at the code, String.compareTo now has the signature
int compareTo( String ). Ditto public int compareTo(String
anotherString)

I am puzzled here. It looks as if String.compareTo( Object ) has
disappeared. But how then could old code run under the new JVMs?

In the old days, all you had to do was make sure your columns had
compatible objecs in them, and you could compare any column with
compareTo. OO overrides performed the necessary magic. You did not
need different code for different types of column.

The problem with generics is it forces you to pretend to know at
compile time things you don't know until run time.

--
Roedy Green Canadian Mind Products
http://mindprod.com

The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair.
~ Douglas Adams (born: 1952-03-11 died: 2001-05-11 at age: 49)
 
R

Roedy Green

Using your example of comparing Strings and Double, let's look at String
method:

That is not what I said. The columns must have COMPATIBLE types in
them. e.g. all Strings, all Doubles, though different columns might
have different types.

I just don't want a big case clause to sort a column depending on the
type that is currently in it. It is a reversion to C.

Here some code from CSVSort that stinks.

/**
* Sort on arbitrary list of columns.
* Defines default the sort order for SortableCSVRow Objects.
* Compare this SortableCSVRow with another SortableCSVRow
with JDK 1.5+ generics.
* Compares key then key2 then key3.
* Informally, returns (this-other) or +ve if this is more
positive than other.
*
* @param other other SortableCSVRow to compare with this one
*
* @return +ve if this&gt;other, 0 if this==other, -ve if
this&lt;other
*/
public final int compareTo( SortableCSVRow other )
{
int diff;
// sort over keys till find a non-tie.
for ( int i = 0; i < CSVSort.this.countOfColsToSort; i++ )
{
switch ( CSVSort.this.sortTypes[ i ] )
{
case 's': // case sensitive, default compare
String

if ( CSVSort.this.isAscendings[ i ] )
{
/* ascending */
diff = ( ( String ) this.keys[ i ]
).compareTo( ( String ) other.keys[ i ] );
}
else
{
/* descending */
diff = ( ( String ) other.keys[ i ]
).compareTo( ( String ) other.keys[ i ] );
}
break;

case 'i':
// case insensitive
if ( CSVSort.this.isAscendings[ i ] )
{
/* ascending */
diff = ( ( String ) this.keys[ i ]
).compareToIgnoreCase( ( String ) other.keys[ i ] );
}
else
{
/* descending */
diff = ( ( String ) other.keys[ i ]
).compareToIgnoreCase( ( String ) this.keys[ i ] );
}
break;

case 'n': // represented as Double

if ( CSVSort.this.isAscendings[ i ] )
{
/* ascending */
diff = ( ( Double ) this.keys[ i ]
).compareTo( ( Double ) other.keys[ i ] );
}
else
{
/* descending */
diff = ( ( Double ) other.keys[ i ]
).compareTo( ( Double ) this.keys[ i ] );
}
break;

case 'x': // represented as Long

if ( CSVSort.this.isAscendings[ i ] )
{
/* ascending */
diff = ( ( Long ) this.keys[ i ]
).compareTo( ( Long ) other.keys[ i ] );
}
else
{
/* descending */
diff = ( ( Long ) this.keys[ i ]
).compareTo( ( Long ) other.keys[ i ] );
}
break;

default:
throw new IllegalArgumentException( "Program
bug: invalid SortType: " + sortTypes[ i ] );
}
if ( diff != 0 )
{
return diff;
}
}

return 0;
}

--
Roedy Green Canadian Mind Products
http://mindprod.com

The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair.
~ Douglas Adams (born: 1952-03-11 died: 2001-05-11 at age: 49)
 
J

Joshua Cranmer

I am puzzled here. It looks as if String.compareTo( Object ) has
disappeared. But how then could old code run under the new JVMs?

It's an automatically-generated bridge method. You can use javap to
convince yourself that it exists.
 
A

Alessio Stalla

Using your example of comparing Strings and Double, let's look at String
method:

That is not what I said.  The columns must have COMPATIBLE types in
them. e.g. all Strings, all Doubles, though different columns might
have different types.

I just don't want a big case clause to sort a column depending on the
type that is currently in it.  It is a reversion to C.

Here some code from CSVSort that stinks.

   /**
         * Sort on arbitrary list of columns.
         * Defines default the sort order for SortableCSVRow Objects.
         * Compare this SortableCSVRow with another SortableCSVRow
with JDK 1.5+ generics.
         * Compares key then key2 then key3.
         * Informally, returns (this-other) or +ve if this is more
positive than other.
         *
         * @param other other SortableCSVRow to compare with this one
         *
         * @return +ve if this&gt;other, 0 if this==other, -ve if
this&lt;other
         */
        public final int compareTo( SortableCSVRow other )
            {
            int diff;
            // sort over keys till find a non-tie.
            for ( int i = 0; i < CSVSort.this.countOfColsToSort; i++ )
                {
                switch ( CSVSort.this.sortTypes[ i ] )
                    {
                    case 's': // case sensitive, default compare
String

                        if ( CSVSort.this.isAscendings[ i ] )
                            {
                            /* ascending */
                            diff = ( ( String ) this.keys[ i ]
).compareTo( ( String ) other.keys[ i ] );
                            }
                        else
                            {
                            /* descending */
                            diff = ( ( String ) other.keys[ i ]
).compareTo( ( String ) other.keys[ i ] );
                            }
                        break;

                    case 'i':
                        // case insensitive
                        if ( CSVSort.this.isAscendings[ i ] )
                            {
                            /* ascending */
                            diff = ( ( String ) this.keys[ i ]
).compareToIgnoreCase( ( String ) other.keys[ i ] );
                            }
                        else
                            {
                            /* descending */
                            diff = ( ( String ) other.keys[ i ]
).compareToIgnoreCase( ( String ) this.keys[ i ] );
                            }
                        break;

                    case 'n': // represented as Double

                        if ( CSVSort.this.isAscendings[ i ] )
                            {
                            /* ascending */
                            diff = ( ( Double ) this.keys[ i ]
).compareTo( ( Double ) other.keys[ i ] );
                            }
                        else
                            {
                            /* descending */
                            diff = ( ( Double ) other.keys[ i ]
).compareTo( ( Double ) this.keys[ i ] );
                            }
                        break;

                    case 'x': // represented as Long

                        if ( CSVSort.this.isAscendings[ i ] )
                            {
                            /* ascending */
                            diff = ( ( Long ) this.keys[ i ]
).compareTo( ( Long ) other.keys[ i ] );
                            }
                        else
                            {
                            /* descending */
                            diff = ( ( Long ) this.keys[ i ]
).compareTo( ( Long ) other.keys[ i ] );
                            }
                        break;

                    default:
                        throw new IllegalArgumentException( "Program
bug: invalid SortType: " + sortTypes[ i ] );
                    }
                if ( diff != 0 )
                    {
                    return diff;
                    }
                }

            return 0;
            }


You're not forced to use that idiom. Rather than casting to concrete
types, cast to Comparable:

if ( CSVSort.this.isAscendings[ i ] )
{
/* ascending */
diff = ( ( Comparable ) this.keys[ i ]
).compareTo( other.keys[ i ] );
}
else
{
/* descending */
diff = ( ( Comparable ) this.keys[ i ]
).compareTo( other.keys[ i ] );
}

you will of course get unchecked warnings, because the compiler won't
be able to enforce that compareTo is called on compatible types.
That's a case when the SuppressWarnings annotation is necessary.

Alessio
 
M

markspace

Roedy said:
switch ( CSVSort.this.sortTypes[ i ] )
{
case 's':


Wow. You're an experienced programmer and the best you can come up with
is to encode column types as a character? And then switch on it?
That's really sad Roedy. Seriously, it's beneath anyone beyond a first
year student.

Why don't you make a real effort, then I'll comment. I'm not going to
write your CSV class for you, and this feels like a vaguely transparent
attempt to get someone to refactor your code for you. "Do your own
homework." It's the only way for you to learn better programming habits.

Here's a hint: There's lots of comments on this list about what to do if
you are using a switch on types in an OO language like Java.
 
L

Lew

Roedy said:
String DOES implement Comparable<String>. Nobody claimed it did not.

I read markspace's comment as an implicit claim that it didn't.
The problem is when you want to use the Comparable the way you did
before generics, where you compare any two Objects and the JVM sorted
out which compareTo method to use depending on the type of the Object.

But if you had different types in the column you'd get a run-time exception.
That's bad. In the good new days, you use generics to catch that at compile
time. Since, presumably, you know the type of the column's contents, you can
make things play nicely before you reach run time.
You can't cast a Comparable<String> to a Comparable<Object>

Nor should you want to in this case. If the column contains 'String' data,
then you stay with 'Comparable<String>'.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top