0

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"); } 
0

1 Answer 1

1

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")); } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot for the explanation Andy ! I knew something weird happened but couldn't explain it ;) Thanks for the upcast method too, could be handy in the future !

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.