> > I suppose that could be avoided by the use of private constructors and > public static factory methods (different method names means there's no > overloading, so no ambiguity in when signatures need to differ only by > generic type argument). But we'd have to choose names for those static > methods -- that's easy when you know that DeleteTopicsResult supports > by-name and by-id variants, but we didn't know that originally, so it's > likely those methods would end up with poor names. So it's not much of an > improvement over public constructors, in my opinion. >
Replying to myself... it's not so bad, because we'd use public constructors initially and only provide factory methods when we needed to evolve the API in a way such that overloaded constructors wouldn't work (e.g. due to erasure ambiguity). Thus, the need to choose names for factory methods only arises once we know how the API is evolving. For example we'd initially have a single public constructor, such as ``` public CreateTopicsResult(Map<String, KafkaFuture<Void>> futures) { ... } ``` and then, when we wanted to change the parameter type argument, we'd switch to factory methods: ``` @Deprecated // use withDescriptions public CreateTopicsResult(Map<String, KafkaFuture<Void>> futures) { ... } // Hide the constructor that has the disambiguated signature behind a static factory method private CreateTopicsResult(boolean disambiguator, Map<String, KafkaFuture<TopicMetadataAndConfig>> futures) { ... } public static CreateTopicsResult withDescriptions(Map<String, KafkaFuture<TopicDescription>> futures) { ... } ``` Obviously the hidden constructor can have whatever signature we want (that doesn't collide with the existing public one), so we could use multiple parameters (as in the DeleteTopicsResult change) if there wasn't a way to convert one parameter to another.