views:

156

answers:

4

For example, I use a method Measure.doubleValue(Unit<?> unit) which returns the double value of a measurement, expressed in the specified Unit. If I pass a Unit<?> variable to it, I get the still very cryptic (to me) error message:

The method doubleValue(Unit<capture#27-of ?>) in the type Measurable<capture#27-of ?> is not applicable for the arguments (Unit<capture#28-of ?>)

I would appreciate if someone could explain what that #27-of ? (or any other number) means, and if there is an elegant way to get rid of this. Thus far, I remove the <?> and set the calling method @SuppressWarnings("unchecked") (so I pass an unchecked Unit instead of a Unit<?>) and everything works as desired, but I'm just curious about this, and I feel like suppressing warnings is not a good practice (isn't it a little like empty catch blocks?).

Thanks!

Edit: Adding some code.

(I'm sorry, this is quite long, but it explains in details what I'm stuck on.)

I am using JSR-275 version 0.9.4 (most recent).

So... If I write this (very dumb example):

Measure measure = Measure.valueOf("3 m");
measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));
System.out.println(measure);

It works and prints "0.0030 km". But I get warning "Measure is a raw type. References to generic type Measure<Q> should be parameterized" over the first Measure occurrence and warning "Type safety: The method doubleValue(Unit) belongs to the raw type Measurable. References to generic type Measurable<Q> should be parameterized" over measure.doubleValue(Unit.valueOf("km")).

Seeing these warnings, I thought I could adjust this way (first line only):

Measure<Length> measure = Measure.valueOf("3 m");

And then I get error message on right part of assignation "Type mismatch: cannot convert from Measure<capture#1-of ?> to Measure<Length>". It (Eclipse) offers me to cast the right part to (Measure&lt;Length>). But then I get warning message over the right part "Type safety: Unchecked cast from Measure<capture#1-of ?> to Measure<Length>". Fix suggested: @SuppressWarnings which I would prefer to avoid (for paranoid reasons I guess).

So, I step back to Measure measure = Measure.valueOf("3 m"); and try to give wildcard to Measure, as obviously, it doesn't know what "3 m" means at this moment. It could be a Length, but also a Mass or a Time. So I get:

Measure<?> measure = Measure.valueOf("3 m");

And no warning or error on this line; fantastic. But, on the second line:

measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));

I get and error message for doubleValue: "The method doubleValue(Unit<capture#3-of ?>) in the type Measurable<capture#3-of ?> is not applicable for the arguments (Unit<capture#4-of ?>)". It suggests to cast Unit.valueOf("km") as a (Unit<?>). Fine. Now I get error message at the exact same location: "The method doubleValue(Unit<capture#3-of ?>) in the type Measurable<capture#3-of ?> is not applicable for the arguments (Unit<capture#5-of ?>)". Notice that the numbers have changed, so that's not the exact same parameters, but a similar reason. Then it does the exact same suggestion which leads to no change whatsoever in the code, since it has already been done.

So that's what is bugging me. The only way to get it working seems to @SuppressWarnings or just ignore them. Isn't it strange?

+2  A: 

The wildcard in Java generics means "unknown type". Here, you define the doubleValue() method as accepting a Unit<something1> where the something1 is not specified by the method. You then pass a value that the caller knows as Unit<something2> for some unknown something2. The compiler error message means that there is nothing which guarantees that the something1 and the something2 designate the same thing.

Try this:

<T> double doubleValue(Unit<T> unit)
{
    ...
}

which means that doubleValue() does not care about what T is.

Thomas Pornin
A: 

I can not reproduce your problem, i.e. the following compiles fine (at least with eclipse):

static double doubleValue(Unit<?> unit) {
    return 0;
}

static void bla(Unit<?> u) {
    doubleValue(u);
}

public static void main(String[] args) {
    doubleValue(new Unit<String>());
}
meriton
+3  A: 
erickson
I am not 100 % sure about how these classes really are defined. A problem, with this library, is that we have the latest code, but we can't find the same version API, nor the same version code. Well, yes for the code, but it's not identical to the bytecode classes for some reason... (Some features and method signature are not the same.)
M. Joanis
+1  A: 

To expand on the other (excellent) answers, but be a bit more JSR-275 specific (I'm using it for a project at the moment).

This bit is interesting

Fix suggested: @SuppressWarnings which I would prefer to avoid (for paranoid reasons I guess).

You're right to be paranoid, but think about it: you're telling the Measure class to parse an arbitrary String, and return a Measure of any type. Obviously in that case you may or may not get a Measure<Length> (you might pass "3 kg") so the only option left to the library is return Measure<?>. If you WANT a Measure<Length>, then you have to coerce it to one somehow -- this can't possibly be guaranteed safe at compile-time.

In this case, I would argue that @SuppressWarnings is perfectly acceptable, assuming you know that the string will always be a valid length. If not, you're delaying an inevitable ClassCastException until later, which is pretty bad.

But even better (hooray!) JSR-275 does give you a way around this, which pushes the error handling to the 'right' place (at the time of obtaining the Measure, not at some point afterwards when it's used). Try

Measure<Length> = Measure.valueOf("3 m").asType(Length.class);

asType returns the appropriate generic version of Measure, or fails with an exception if the dimension of the parsed unit is NOT length -- this is almost certainly what you want, right?

I think this solves your problem.

Cowan
You're right, `.asType()` would fix the problem. Very good answer indeed.But, in my case, I still have a problem, because my extension of the JSR-275 library is supporting conversion between different quantities. In the optical domain, for example, we convert between nm, THz, Ky, etc. directly, so I can't force a type to my measures... But that's another story. Thank you!
M. Joanis