I have an interface specifying method that takes a generic type as input which is used to create a URL.
interface UrlGenerator<T> {
String prepareUrl( T input );
}
There is one implementation that doesn’t need the parameter. It uses Void for generic type T.
class StaticUrlGenerator implements UrlGenerator<Void> {
private final String url;
public StaticUrlGenerator( String url ) {
this.url = url;
}
@Override
public String prepareUrl( Void nothing ) {
return url;
}
}
The StaticUrlGenerator is awkward to use, as it requires null as an argument to the prepareUrl method.
I could lose the input parameter:
interface UrlGenerator<T> {
String prepareUrl( T input );
}
Now I have to pass the required input to the implementing class in other way (in constructor). This way I lose the stateless nature of the class, I have to recreate it with different constructor arguments every time I want to change the input.
class SchedulePageUrlGenerator implements UrlGenerator {
public static final String QUERY_STRING_BASE = "?from=";
private final String showingBaseUrl;
private final LocalDate date;
public SchedulePageUrlGenerator( String showingBaseUrl, LocalDate date ) {
this.showingBaseUrl = showingBaseUrl;
this.date = date;
}
@Override
public String prepareUrl() {
DateTimeFormatter fmt = DateTimeFormat.forPattern( "yyyy-MM-dd" );
String dateStr = fmt.print( date );
return showingBaseUrl + QUERY_STRING_BASE + dateStr;
}
}
I think there must be something fundamentally wrong with my design.
The only thing wrong is that you are trying to conflate a one argument method and a zero argument method. You just can’t do it in Java … without opening the door to other problems.
Basically you’ve got three choices:
Stick with your current approach and explicitly pass
nullin the Void case.Add a second (no argument) method to the interface to deal with the Void case, and make it call the one argument method with a
null. Your code needs to cope with anullwhenTis notVoid, but it did anyway.Refactor the interfaces so that there are two distinct interfaces, one with a
String prepareUrl()and the other withString prepareUrl(T), and implement the former as a special case class.Personally, options 2 is slightly better than option 1, but the 3rd option is probably going to lead to other problems; e.g. that particular method with its two variants will be an impediment to polymorphic method calls over the entire space of
Ttypes.(Varargs is a bad idea, because that opens the door for multiple arguments which is probably meaningless for your problem.)