I am facing a confusing issue in my code.
I have one method which signature is
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure);
In another class, I have this method, which calls the previous one :
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
APIInterface.getInstance().sendNewMeasure(measure.getClass(), measure);
}
The error I get is Wrong argument type, found 'T', required <? extends com.blablabla.Measure> but I don't get why, as the measure object is of type T which extends Measure.
Is there any way to fix this, and most importantly, why is it not working ?
Thanks !
EDIT :
This is the implementation of the sendNewMeasure method :
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure) {
String measureType = measure.getJSONMeasureTypeName();
List<T> measures = T.find(type, measure.timestamp, true, false);
measures.add(measure);
sendMeasures(siteId, sensorId, measureType, measures);
}
EDIT 2 : And this is the find method signature, the one I cannot change:
public static <T> List<T> find(Class<T> type, int timestamp, boolean includeStart, boolean inclueEnd);
The type of .getClass() is not what you think it is. .getClass() returns Class<? extends |X|>, where |X| is the erasure of the static type of the expression it's called on. In this case, measure has static type T, which has erasure Measure, so measure.getClass() has type Class<? extends Measure>, i.e. the type parameter is an unknown subtype of Measure, and .sendNewMeasure(measure.getClass(), measure) there is no way the compiler can guarantee that measure (of type T) is an instance of this unknown type.
Basically, the problem is that .getClass() loses type information. Its return type is not directly linked to the type of the thing it is called on, because the Java type system cannot express the concept of "the real runtime type of the thing it's called on". However, intuitively, you know that the call to the method with the current signature .sendNewMeasure(measure.getClass(), measure) is type-safe, because the type of measure.getClass() should really be Class<U> where U is the real runtime class of measure, and you know that measure is obviously an instance of that same type U, so there exists some type argument, this U (which is not necessarily the same as T) for which the call to .sendNewMeasure() type-checks, but the question is how to convince the compiler of this without using unchecked operations.
The problem is that the type returned by measure.getClass() is not sufficiently linked to the type of measure. One way to re-link them is to use the class to cast the object to its type (which will always succeed), using the class's method .cast(). But it doesn't help to do this with an expression of type Class<? extends Measure>, because the resulting of .cast() is ? extends Measure which just degrades to Measure, so we still don't have a link between the two types. We need a real name for the type, not a wildcard, for us to maintain this link. The way to turn a wildcard into a named type is capture, which requires passing it into a generic method:
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
helper(measure.getClass(), measure);
}
private <U extends Measure> void helper(Class<U> clazz, Measure measure) {
U castedMeasure = clazz.cast(measure);
APIInterface.getInstance().sendNewMeasure(clazz, castedMeasure);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With