Reputation: 1215
I am trying to define a class which type could be a subtype of the given one if inferred but it doesn't seem to work with the default Java type inference mechanism and I do not understand why.
Here are some relevant pieces of code to illustrate the situation
public class ObjectChanged<T extends Something> implements TriggeringCondition<T> {
private final Class<? extends T> type;
private String featureName = null;
protected ObjectChanged(Class<? extends T> type) {
this.type = type;
}
public ObjectChanged<T> onFeature(String featureName) {
this.featureName = featureName;
return this;
}
public static <X extends Something> ObjectChanged<X> objectChanged(Class<? extends X> type) {
return new ObjectChanged<>(type);
}
}
Let's say I have one class called FastCar extending Car. I would like to build an object change for a FastCar, but to downcast it to TriggeringCondition<Car>
.
If I write the following code it works as expected
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class);
}
But then if I call the onFeature(String)
method it doesn't compile anymore and complains that my triggering condition if of type FastCar, which is not compatible with Car.
If now I define the objectChanged
function like this
public static <X extends Something, Y extends X> ObjectChanged<X> objectChanged(Class<Y> type, Class<X> baseType) {
return new ObjectChanged<>(type);
}
Then I can use this code which resolves the problem
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class, Car.class).onFeature("something");
}
I also found out I can fix the previous build issue with this syntax, but it's quite ugly imo.
TriggeringCondition<Car> test() {
return ObjectChanged.<Car> objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class).onFeature("test");
}
Upvotes: 0
Views: 47
Reputation: 140319
Is there a way to write the test method like this without needing an extra parameter ?
No.
If you don't want to use the type witness (<Car>
), all you can do is to assign the objectChanged
result to a variable, and then call onFeature
on that variable.
TriggeringCondition<Car> test() {
TriggeringCondition<Car> tc = objectChanged(FastCar.class);
return tc.onFeature("test");
}
This is a problem which crops up a lot if you use Guava's Immutable*.Builders:
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("foo")
.build();
The type witness is needed here, otherwise the type of the Builder is inferred to be ImmutableList.Builder<Object>
, because the type of the polyexpression is determined before the .add(String)
call.
It's annoying, but that's the nature of the beast.
One thing you could do is to define a static upcast
method:
static <T extends Something> ObjectChanged<T> upcast(ObjectChanged<? extends T> oc) {
ObjectChanged<T> result = new ObjectChanged<>(oc.type);
return result.onFeature("test");
}
Now you can invoke something like:
TriggeringCondition<Car> test() {
return upcast(objectChanged(FastCar.class).onFeature("test"));
}
Upvotes: 1