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 safet开发者_运维技巧y: 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<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?
Does the measure class look something like this?
class Measure<T> {
double doubleValue(Unit<T> unit) {
...
}
}
In that case, have a reference type of Measure<?>
doesn't mean that you are allowed to pass any type of Unit
to the doubleValue
method. Rather, it means that the Measure
instance has an unknown generic type, and it isn't safe to pass a Unit
to its doubleValue
method because the compiler cannot ensure that the types are compatible.
A wildcard does not mean "any type"; it means "unknown type".
Update:
The valueOf(CharSequence)
returns an unknown type of Measure
—a Measure<?>
. To convert this safely to the type of Measure
you expect, you must use the Measure.asType()
method. Likewise with the target Unit
, created by the Unit.valueOf(CharSequence)
method.
Measure<?> unknownMeasure = valueOf("3 m");
Unit<?> unknownUnit = Unit.valueOf("km");
Measure<Length> length = unknownMeasure.asType(Length.class);
Unit<Length> kilometer = unknownUnit.asType(Length.class);
length.doubleValue(kilometer);
Look at the examples in the Measure
class documentation. They will provide some additional depth.
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.
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.
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>());
}
精彩评论