This one hit me at work. I was modifying the facade of the subsystem I'm working on. I needed to go from: public RegistrationId register(User user, String param1, String param2) to public RegistrationId register(User user, String param1, Long param2). I wanted to do this without breaking anyone else's code, so I deprecated the first method, wrote the second, and checked it in.
I figured that was a pretty safe bit of interface evolution. I was wrong. Half an hour later, I was informed I'd broken the build.
The problem was: ‘null’ was a valid value for param2. Because null has an indeterminate type, the compiler couldn't tell which of the two selectors was being referred to by the calling code. New code could get around this by casting null to the appropriate type: (foo.register(myUser, "blah", (Long) null);), but existing code didn't have that cast.
Of course, If I'd been slightly less lazy in checking in the new interface, of if the system I was working on wasn't so freaking huge, I'd have caught it myself. C'est la vie. Still, it's something to look out for.
Update: Yes, I know this is my fault. There are extenuating circumstances, but to list them would involve going into more detail about what I'm doing at work than I am comfortable with. I just brought it up because it's an interesting gotcha for people dealing with (statically typed) published interfaces.
This is only a gotcha in Java, thank goodness. In C++ you could've provided a default value for the third paramter in the original method, and the compiler would have therefore defaulted to the original method:
public:
RegistrationID register(User user, std::string param1,
std::string param2 = "")
Are they intending to provide Java with default values at some stage? I hear they're finally going to give you guys generic programming (templates?).
I don't know of any plans for Java to get default parameter values: method overloading is considered sufficient:
public RegistrationId register(User user, String param1) {
this.register(user, param1, "");
}
Default parameters are evil! There are things you can do with them in C++ that will make your hair fry or turn grey!
Besides, why not use a long instead of a Long?
I want to be able to accept 'null' as a valid "I am deliberately not setting this" value. Can't do that with primitives.
Hmm, I prefer to see this "gotcha" as a design flaw in the original interface.
There should have been a method public RegistrationId register(User user, String param), which resolves to return register(user, param, null). The ambiguity would have been solely in your own class then.
Just my 0.02 Euro
Fokko
I sympathize with this kind of static typing flaw... but I regard "null" arguments as evil. I would have overloaded the method so that the one that is missing the string / long parameter does what you want.
Whenever I see "null" being used as a special value, I consider using the NullObject pattern instead.
Next time you are worried about not breaking users' code, try to build the entire product before submitting your change :-)
--
Cedric
Are nulls evil for being typeless? Is Charles evil for not doing a full build before checking in? Is the system evil for being freaking huge? Or is Java evil for allowing method overloading?
So much blame to smear around. So little time...
I am evil. It has been conclusively proven: http://www.livejournal.com/users/charles/164849.html
Having read the above I must agree. you are evil
Hey why didnt you just add a method that took an Object o, then deferred the type checking to inside the method. That way you can proliferate long switches and chained if then elses....arg I've been swallowed by the hude frea*king architecture..
Just so long as you're not putting data and logic in the same place, you should be fine...