Suppose I have a class structure looking like this:
public abstract class Interval<I extends Interval<?>> { public static final IntervalFactory<? extends Interval<?>> factory = null; // some variables and methods } public class Week extends Interval<Week> { public static final IntervalFactory<Week> factory = new WeekFactory(); // some variables and methods } public interface IntervalFactory<I extends Interval> { I containing(LocalDate date); List<I> containing(LocalDate startDate, LocalDate endDate); } public class WeekFactory implements IntervalFactory<Week> { @Override public Week containing(LocalDate date) { // implementation } @Override public List<Week> containing(LocalDate startDate, LocalDate endDate) { // implementation } }
Now I have a class that uses those intervals and factories:
public class SomeClass<I extends Interval<?>> { private final IntervalFactory<I> intervalFactory; public DistributorForSlowQuantifier() { this.intervalFactory = (IntervalFactory<I>) I.factory; } public Map<I, Double> distribute(double value) { // implementation } }
Is this the right way of approaching the problem if a class needs to create instances from a type variable?
Is there a way around the cast in the SomeClass
constructor?
The obvious things don’t work – having a wildcard as the type parameter of intervalFactory in SomeClass
loses all the type information, the factories are static, so I cannot use I
as a type parameter, and if I don’t mark them as static, I need an instance of I to access the factory…
The cast is absolutely safe, as long as Interval implementations choose the “corresponding” factory, but it still feels… wrong.
Advertisement
Answer
The cast in the SomeClass
constructor doesn’t even work. It’ll always use Interval.factory
, whether or not that corresponds to the kind of factory you want. I.factory
does not do what you think.
By far the most common and sensical approach: to pass in the appropriate factory to the constructor. You can add some syntactic sugar around this with static imports and static factory methods to make it look like e.g. new DistributorForSlowQuantifier(days())
or the like. There are other, much more convoluted and messy workarounds, but all things considered, they’re worse.