Unchecked call to compareTo

  • Thread starter Russell Wallace
  • Start date
R

Russell Wallace

I'm trying to sort some data returned from a database (in rows and
columns, where a column may be BigDecimal, String or whatever, something
that implements compareTo anyway), and I'm fine up until the actual
comparison function. This is as close as I've got with it:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

gives:

warning: [unchecked] unchecked call to compareTo(T) as a member of the
raw type java.lang.Comparable

How do I get rid of the warning?

Thanks,
 
K

Knute Johnson

Russell said:
I'm trying to sort some data returned from a database (in rows and
columns, where a column may be BigDecimal, String or whatever, something
that implements compareTo anyway), and I'm fine up until the actual
comparison function. This is as close as I've got with it:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

gives:

warning: [unchecked] unchecked call to compareTo(T) as a member of the
raw type java.lang.Comparable

How do I get rid of the warning?

Thanks,

Are you trying to use the same Comparator for different types? Does it
work? Take a look at Roedy Green's website for a complete description
of warning suppression.

http://mindprod.com/jgloss/annotations.html#SUPPRESSWARNINGS
 
D

Daniel Pitts

Russell said:
I'm trying to sort some data returned from a database (in rows and
columns, where a column may be BigDecimal, String or whatever, something
that implements compareTo anyway), and I'm fine up until the actual
comparison function. This is as close as I've got with it:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

gives:

warning: [unchecked] unchecked call to compareTo(T) as a member of the
raw type java.lang.Comparable

How do I get rid of the warning?

Thanks,

I don't think there is a good and simple way to do that.
You could generify Record, generify compare(), generify your class
containing compare(), or suppress the warning usign @SuppressWarning

Hope this helps,
Daniel.
 
L

Lew

Please do not embed TAB characters in your posts.

I do not believe that suppressing the warning here is the answer, but instead
to declare the method with generic types in the first place.

Something along the lines of

<T extends Comparable<? super T>> int compare( T a, T b )
{
return a.compareTo( b );
}

The exact form depends on what you actually are trying to do: compare Records?
Compare a type buried in a Record? Compare the exact type, or things that
extend a common supertype?

You cannot cast with a generic because of type erasure, but here you likely do
not need such a cast.

Warning: I am still rough with generics, and I have not tried the code I
suggested.

- Lew
 
R

Russell Wallace

Lew said:
I do not believe that suppressing the warning here is the answer, but
instead to declare the method with generic types in the first place.

My background is mostly in C/C++ where a warning can save you spending
three hours tracking down an errant pointer bug, so I'm in the habit of
thinking code isn't right until it compiles cleanly without suppressing
any warnings, so I'd like to use some appropriate declaration to solve
the problem if reasonably possible.
The exact form depends on what you actually are trying to do: compare
Records? Compare a type buried in a Record? Compare the exact type, or
things that extend a common supertype?

Just plain old Objects. The idea is that there's data in rows and
columns, each column may contain BigDecimals, Strings, Dates or
whatever, and the intent is to sort records by the values in the
currently selected column. So in:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

ca represents record a's value in the currently selected column, ditto
cb for record b. ca and cb may be BigDecimal, String or whatever, but
they are guarenteed to be the same type (though I don't know how or if I
can tell the compiler that).
 
L

Lew

Russell said:
Just plain old Objects. The idea is that there's data in rows and
columns, each column may contain BigDecimals, Strings, Dates or
whatever, and the intent is to sort records by the values in the
currently selected column. So in:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

ca represents record a's value in the currently selected column, ditto
cb for record b. ca and cb may be BigDecimal, String or whatever, but
they are guarenteed to be the same type (though I don't know how or if I
can tell the compiler that).

Now you are definitely in the "<T ...> compare( T, T )" world.

How about you do the get outside the method and pass the comparables into the
method, so that the method is responsible for only one task, not both
extraction and comparison?

Then you don't even need the comparison method, since compareTo already works.

Object oa = get( a );
Object ob = get( b );
if ( oa.compareTo( ob ) ) { ... } // if ( get(a).compareTo( get(b) ) ) ...

- Lew
 
R

Russell Wallace

Lew said:
Object oa = get( a );
Object ob = get( b );
if ( oa.compareTo( ob ) ) { ... } // if ( get(a).compareTo( get(b) ) ) ...

I tried that before, but it's an error since compareTo is a method of
Comparable, not Object.
 
L

Lew

Russell said:
I tried that before, but it's an error since compareTo is a method of
Comparable, not Object.

of course - sorry.

I just know I've seen the answer to this somewhere. Time to google.

- Lew
 
T

Tom Hawtin

Russell said:
Just plain old Objects. The idea is that there's data in rows and
columns, each column may contain BigDecimals, Strings, Dates or
whatever, and the intent is to sort records by the values in the
currently selected column. So in:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

ca represents record a's value in the currently selected column, ditto
cb for record b. ca and cb may be BigDecimal, String or whatever, but
they are guarenteed to be the same type (though I don't know how or if I
can tell the compiler that).

I think you need to restrict the columns to particular comparable type.
So you comparator ends up something like this:

class MyComparator<T extends Comparator<T>>
implements Comparator<Record>
{
private final Class<? extends T> clazz;

public MyComparator(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException();
}
this.clazz = clazz;
}
public int compare(Record a, Record b) {
T ta = clazz.cast(get(a));
T tb = clazz.cast(get(b));
return ta.compareTo(tb);
}
...
}

(Disclaimer: I have not so much as attempted to compile that.)

Where's the catch? Well clazz cannot be a generic type (although T may
be). However, you are fine with BigDecimal, String, Date, etc.

Tom hawtin
 
P

Patricia Shanahan

Russell said:
My background is mostly in C/C++ where a warning can save you spending
three hours tracking down an errant pointer bug, so I'm in the habit of
thinking code isn't right until it compiles cleanly without suppressing
any warnings, so I'd like to use some appropriate declaration to solve
the problem if reasonably possible.


Just plain old Objects. The idea is that there's data in rows and
columns, each column may contain BigDecimals, Strings, Dates or
whatever, and the intent is to sort records by the values in the
currently selected column. So in:

int compare(Record a, Record b) {
Comparable ca = (Comparable<?>) get(a);
Comparable cb = (Comparable<?>) get(b);
return ca.compareTo(cb);
}

ca represents record a's value in the currently selected column, ditto
cb for record b. ca and cb may be BigDecimal, String or whatever, but
they are guarenteed to be the same type (though I don't know how or if I
can tell the compiler that).

Maybe the warning suppression annotation really does make sense in this
case.

Isn't it the simplest, most direct way to tell the compiler that there
is additional data, contained in the structure of the program but not
accessible to the compiler, that ensures the objects are Comparable to
each other?

You could add a comment explaining why the warning should be suppressed.

Patricia
 
R

Russell Wallace

Patricia said:
Maybe the warning suppression annotation really does make sense in this
case.

Isn't it the simplest, most direct way to tell the compiler that there
is additional data, contained in the structure of the program but not
accessible to the compiler, that ensures the objects are Comparable to
each other?

I'm actually coming to agree with that conclusion, particularly since
from the page Knute linked to (thanks Knute!) I find it's possible to
suppress just the one type of warning for just the one method, which is
much cleaner than global suppression; so unless it turns out there's a
better way to do it, that's the solution I'm going with.
 
T

Tom Hawtin

Tom said:
class MyComparator<T extends Comparator<T>>
^^^Comparable...
implements Comparator<Record>
{
private final Class<? extends T> clazz;

public MyComparator(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException();
}

Or:
if (clazz.getTypeParameters().length != 0) {
throw new IllegalArgumentException(
"Casts of erased generic type cannot be checked"
);
}
this.clazz = clazz;
}
public int compare(Record a, Record b) {
T ta = clazz.cast(get(a));
T tb = clazz.cast(get(b));
return ta.compareTo(tb);
}
...
}
Where's the catch? Well clazz cannot be a generic type (although T may
be). However, you are fine with BigDecimal, String, Date, etc.

Looking through the Java library, there doesn't appear to be any cases
where that is a problem. Although you could make up cases. For instance:

interface ComparableList<E extends Comparable<? super E>>
extends List<E>, Comparable<ComparableList<E>>
{
}

Tom Hawtin
 
J

John Ersatznom

Russell said:
I'm actually coming to agree with that conclusion, particularly since
from the page Knute linked to (thanks Knute!) I find it's possible to
suppress just the one type of warning for just the one method, which is
much cleaner than global suppression; so unless it turns out there's a
better way to do it, that's the solution I'm going with.

It turns out there's a better way to do it.

First generify Record:

public class Record<T extends Comparable<? super T>> {
private T inner;
...
private T get () { return inner; }
}

Next make your comparator:

public class RecordComparator<T> implements Comparator<Record<T>> {
public int compare (Record<T> x, Recort<T> y) {
return x.get().compareTo(y.get());
}
}

x.get() and y.get() are both T, and as T extends Comparable<? super T>,
the compareTo should work. It just requires that you can generify
Record. Of course you could just add a compareTo to the Record class and
have

public class Record<T, U extends Comparable<T>>
implements Comparable<Record<T, U>> {
private U inner;
...
private U get () { return inner; }
public int compareTo (Record<T, ? extends T> x) {
return get().compareTo(x.get());
}
}

Then you have U.compareTo(? extends T) being called on things that
implement Comparable<T> with U extending T. Should work.

The get method needn't return an "inner" private field; it can access a
database or what-have-you. Presumably that or future-proofing is the
reason for the get method instead of exposing the field.
(Future-proofing value is why this is good OO practise to begin with.)

If you can't control Record, you may be able to extend it. Or at least
wrap it:

public class MyRecord<T, U extends Comparable<T>>
implements Comparable<Record<T, U>> {
private Record inner;
MyRecord (Record x) { inner = x; }
... any other methods that thunk through to inner
@SuppressWarnings("unchecked")
private U get () { return (U)inner.get(); }
public int compareTo (Record<T, ? extends T> x) {
return get().compareTo(x.get());
}
}

Now that nasty, foul-smelling cast is encapsulated in a single method
call with a single evil, throbbing @SuppressWarnings("unchecked")
attached, and everything else can use the generic MyRecord, aside from
having to get an icky ungeneric Record to immediately quarantine inside
a new MyRecord from time to time. Just remember that it won't blow up if
you put the wrong Record in until you call get(). Put an assert(get()
instanceof whatever) after every constructor call and have your unit
tests watch for it to throw exceptions -- ClassCast *or* assertion failure.

If there's called code that really needs the original Record object, you
can implement a poison-seeping goo-oozing Record-returning getRecord()
method if you *really really have to* to pass to the older nongeneric
code. Just be sure to use the nifty Unicode-in-identifiers feature of
Java to put a glowing pentagram in the method name somewhere and locate
it at line 666 of the source file, mmmkay?
 
R

Russell Wallace

John said:
If there's called code that really needs the original Record object, you
can implement a poison-seeping goo-oozing Record-returning getRecord()
method if you *really really have to* to pass to the older nongeneric
code. Just be sure to use the nifty Unicode-in-identifiers feature of
Java to put a glowing pentagram in the method name somewhere and locate
it at line 666 of the source file, mmmkay?

*laughs* Cute! Thing is... it's not actually true that everything in a
Record will be Comparable. Consider BLOB fields for example. One will of
course refrain from attempting to use a BLOB as a sort key, but the
compiler can't be expected to know that; I'm taking the SuppressWarnings
on compare() as the way to reassure the compiler of this fact.
 
D

Daniel Pitts

Russell said:
*laughs* Cute! Thing is... it's not actually true that everything in a
Record will be Comparable. Consider BLOB fields for example. One will of
course refrain from attempting to use a BLOB as a sort key, but the
compiler can't be expected to know that; I'm taking the SuppressWarnings
on compare() as the way to reassure the compiler of this fact.

public class Record<T> {...}

public class ComparableRecord<T extends Comparable<T>> extends
Rector<T> implements Comparable<ComparableRecord<T>> {
T inner;
public int compareTo(ComparableRecord<T> o) {
return inner.compareTo(o.inner);
}
}

That'll show that pesky compiler. AND prevent you from even accidently
trying to compare blobs.
 

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

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top