- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
long? saveComment(frist)
Admin
You forgot to make frist nullable.
Admin
Returning null instead of throwing an exception is a perfectly valid design pattern. Exceptions are thrown up until they need to be handled, and then you handle it and return some error code/message instead. You don't keep throwing up until the application crashes.
Admin
The WinAPI, among others, would disagree with you. It's perfectly reasonable to have both a return value and out params, especially in the case where the return is some sort of status. For that matter, .NET itself has such functions, like TryParse.
Admin
Yes, having a return value and out params can be perfectly fine.
That said, when all the outputs and return are related to each other, which seems to maybe be the case here (??), it's usually a lazy way to avoid defining a class or struct and returning that.
Admin
Honestly, I have to disagree.
In most cases,
null
doesn't have a clear meaning. Speaking generically, it can just as easily mean "unknown", "not applicable", or "error". IfgetGender() == null
, does that mean we just don't have that data for this person, that they're nonbinary, or that this method is doing some fancy lazy-loading and there was a hiccup in database connectivity? And even in data that's always "applicable" (like someone's birthday), you still can't distinguish between something being legitimately unknown and a call failing for some internal reason. And even if something has to be known - like a username - wherenull
can only indicate an error, it still doesn't tell you what the error was!And if you do return
null
, you haven't prevented the error; you're only kicking the can down the road. If the client can deal with not having the value, it cancatch
an exception just as easily as do a null check. If it can't, you'll get a null pointer exception. On the one hand you haven't added any value (and, in a language with checked exceptions, you've also made it easier to forget the check!); on the other, you've actively degraded your ability to debug the application!If the data can be legitimately unknown or not applicable, you can use some flavor of
Maybe
(orOptional
, or whatever your language calls it). Or if you need something easier to distinguish, you can write your own (simple!) abstraction around the return value. Then the error cases can throw an exception, the cases where it's legitimately missing just return an emptyMaybe
, and the happy path just returns the appropriate value.(I will grant that
null
s are okay in private methods, as long as you're careful about them.)Admin
You absolutely do. Or at least, you let them propagate up to a top-level event handler.
Why? If you can't handle an exception (by retrying a network call, maybe), you should leave it up to your client how to deal with it. Maybe the client just lets it propagate, but maybe they can do something else. In the first case, the exception will end up getting logged somewhere (your top-level handlers do log exceptions, right?); in the second case, it's not like it's any harder for the client to
catch
an exception vs. check for null. Conversely, if the client can't handle thenull
, then it has to decide what to do with it... like throwing an exception. Okay, they could just propagate the null... and now we're back to the first problem, thatnull
can't tell you anything besides "oops".What I'm trying to say is - certain low-level considerations aside - throwing an exception isn't any harder than returning
null
, but handlingnull
is harder - for the client - than handling an exception; andnull
s don't have stack traces or detailed messages, so when something does go wrong they're harder to debug. And when a lack of value is legitimate, you can use an abstraction that makes that clear and doesn't let people forget about it.But please, if you still think I'm wrong, argue back! I might be vocal and opinionated, but that doesn't mean I'm not open to having my mind changed. :)
P.S. I'm not saying there are never cases where
null
is correct. I returnnull
fromprivate
methods all the time, for example. Or if you have to interact with legacy code or a serialization framework that expects them. I'm saying those situations are uncommon, not nonexistent.Admin
Yup. It reminded me a lot of my CS 101 students passing the same half-dozen parameters around (and then complaining when they needed another one and had to change all the methods along the way - now that was a teachable moment!).
(As for .NET's
TryParse
, that's always felt almost like an aesthetic concern to me - it's just cleaner to return the "did the conversion work?" flag. On the other hand, I'd rather use anOptionalInt
(or whatever it's called in .NET;int?
, maybe?), but eh.)Admin
I don't agree with a couple of the things here being a WTF.
Having a response value and out parameters can make sense. Returning the ID from a 'create database row' method is a good idea; maybe they have a pattern in their codebase where that is the only thing you should return from them and so other information that may be relevant to calling code should be in out (or ref) params.
Using 'save' and 'update' like that is Hibernate parlance and so while it's confusing it's probably to keep inline with the ORM.
Null is sometimes a valid response value - without seeing the documentation on this method it's hard to tell. For example 'there is no new record because I didn't need one, because you already saved this' would be a valid null result. The null result vs exception is situational - using exceptions for flow control is an anti-pattern so if the 'no record' case is expected under normal operation then returning a null is legit. (I suspect that here it isn't, and it should be an exception, but it's impossible to be sure.)
Admin
Some of us work where our customers don't allow us to use exceptions. shrug
Admin
Sorry but using 'save' and 'update' like that is not even close to how Hibernate uses save and update. In hibernate save is used if you have a transient object, while update is used when you have a detached object. Total different things, and not at all well explained in the documentation.
If your save method can determine that "there is no new record because I didn't need one" it should return the id of that existing record which caused it to determine that no new object was needed. Also: Determining if you need a new record is Business logic, which should not be done by the same method which does the persisting.
Admin
https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/
Admin
.Net TryParse is a workaround, not the ideal design pattern, IIRC the constrains that resulted in TryParse are:
Admin
Love it... you sound like a fantastic teacher. Null provides no context whatsoever, and if you catch yourself returning null, should ask yourself why... what am I trying to model here? Is there something missing that should be taken into consideration?
For instance, my first brush with this conundrum was with "deleting" a user from the database... the admins of the application kept using the terminology "we need to delete a user". So the devs blindly added that feature in by using a database delete query. Then a few months down the road, there were exceptions when a record for a user was gone. References to that user (created by and updated by for instance) no longer pointed to a valid user (no foreign key)... which is when I learned that when a user says delete, they almost always mean something else. In this case it was deactivate a user. Often have to probe to get the WHY, and lazy people just take the face value.
All this to say, returning null is just lazy, instead of understanding the greater context of why you're having to return null and doing it differently. There is a better solution that communicates what is going on in the software.
Admin
That's so nice of you to say!! Thank you!
Admin
That was what I said no? At some point, you need to handle it and not throw again. From a single representative line, you can't say that "it should return an exception instead of null". You don't know where this code is on the stack - maybe it's on the function that just did handle the exception, even showing whatever message to the user, and then returns null because if has nothing more to say. The caller just needs to check if the return is null and NOT use the returned value.
Also, exceptions are expensive as hell. I hate things like "long?", but in high performance code you do NOT want to keep throwing exceptions to signal errors, you return some kind of status code instead.
Admin
Horseshit. That's their opinion. Null to represent an absence of value/object is extremely useful. It's not like no one is using it, or being forced to use it. On C you can't have null strings, just empty strings. But that's not the same thing at all. ALL modern languages support NULL and nullable objects. It's not a mistake, it's a feature.
Example: difference between a blank password (valid), and no password specified at all. NULL represents the latter case perfectly.
Admin
Also, the second link is about null pointers leading to random code execution and security issues, which is not what we're talking about here.
Admin
'In most cases, null doesn't have a clear meaning'
Null means one thing and only one thing. Often it's badly handled/abused.
'Speaking generically, it can just as easily mean "unknown", "not applicable", or "error".'
None of the above. It's nothing, empty, no data.
'If getGender() == null, does that mean we just don't have that data for this person, that they're nonbinary, or that this method is doing some fancy lazy-loading and there was a hiccup in database connectivity?'
No, it means you have no data. If some idiot chooses to represent some condition using null, they're good material for this site.
'my CS 101 students'
Oh, you're a teacher. Makes sense. At least you're reading this stuff and starting to learn.
Admin
'Example: difference between a blank password (valid), and no password specified at all. NULL represents the latter case perfectly.'
No!
Null means you don't have any data. No password specified is a datum.
Admin
The error already happened, catching an exception and throwing a null to represent the same error the exception already represented in a much more detailed way is pointless, it doesn't improve error handling in any way. Returning null doesn't really handle the exception either, as you merely changed the way to communicate the error condition (failure to create a value) and the caller still has to handle that condition.
'You don't know where this code is on the stack' If there's a caller who has to check for null then it's not the top level.
'Null to represent an absence of value/object is extremely useful. ' That's not the case here as this is the code responsible for creating the value.
Admin
Everyone talking about how TryParse should return an int?. What about Dictionary's TryGetValue? You may want to check if anything has been saved against a key in a Dictionary. The values in the Dictionary may be objects. There is a difference between "found the key, and it had a null (representing no object or data, as per Foxglove's comment) saved against it" and "didn't find the key". Both are valid paths, neither of which are errors, so no need for an Exception. Return a null doesn't tell you which it is.
Admin
Are you sure you mean C? In C strings don't really exist. The closest thing to a string in C is char * which is a pointer and as nullable as it gets.
Admin
@Foxglove: Now that you've emphatically told us what you disagree with, how about explaining to us what you think the correct usage(s) of null is/are? We're here to poke fun at the bad examples, but we're also here to use the bad examples to teach and learn the right way.
This sounds like a topic you're passionate about; lay some wisdom on us.
Admin
Also, TryParse should not return int? because it's a boxed value which generates garbage. Generating garbage to make code more readable / understandable can be a perfectly fine choice, but it should be a choice, not made mandatory by core library functions. I need to be able to write garbageless code if it's required.
Admin
Semantics? That's what I said/meant. Sorry if you were just being funny.
Admin
@greg: and there enters the problem of null pointers. I meant more C++, where an std::string cannot be NULL.
Admin
Tony Hoare, the inventer of null, calls it his one-billion dollar mistake (see wikipedia). For you, null always means missing data instead of an error. But you must be aware that this is your own house rule and only a few programmer have the same and stick to it. Most do the opposite: returning an empty array or list means missing data and returning a null means an error happens. In my over 40 years of programming I have seen a lot of code that I did not write myself and guessing the meaning of null in an API method you have to use is always very time consuming. Not to mention the most frustrating NullPointerExceptions that do not give you any clue about the root cause and therefore take longest to fix. I can confirm everything Naomi told you. In our last project a few java programmer and me even went so far not to use null at all. Optionals, empty objects and appropriate exceptions were a good replacement. And after a few years running the code in production it turns out that this code had a lot of fewer bugs and the bugs it still had were a lot of faster to analyze and to fix. That means, in the end it paid off.
Admin
Null means no data was returned. It doesn't have uses. It is the absence of information, not information.
Admin
int? isn't boxed, it is a struct with a field of type int and a field of type bool which says whether the value is null or not, from a performance POV there shouldn't be a big difference between returning bool and an output parameter or returning a ValueType? but you can measure this yourself, and the .Net team could do even better, they could do a Parsed<T>, or Result<T> which also work with reference types, if generics existed before the creation of the TryXXX methods
Admin
[I hope this post formats correctly as I won't be able to edit it]
No uses? When you call some function to get some data with some criteria, and there's no data satisfying the criteria, you need to signal that condition. There are multiple ways to do it:
throw an exception: not an option in high performance code, Exceptions are expensive. Plus, it's not always an error condition to not have data.
return a Status code. This implies you now need an Out argument as well (not a problem, just a design choice).
return an empty array. This implies the function will return an array with 1 element in the normal case (in this particular API where only 1 value is expected).
return a NULL.
In ALL of these cases, the caller needs to handle the response somehow: either catching the exception, check the return code, check the length of the array, or check if the value is NULL. These are just different design patterns and NONE of them is wrong, ALL of them can cause bugs if misused. You use one of them consistently throughout your API and document the hell out of it. That's it.
Admin
Code Refactorer, read my previous answer. You're just talking about lack of documentation in general.
function returns an int status code, with -1 meaning "no data":
versus
I don't see a problem with either approach.
Admin
I question the validity of having an empty password... and a null one for that matter. Does that mean the account should be locked? Does that mean the user is disabled? Does that mean the user is using some other form of authentication? Does it mean this user hasn't finished setting up their account yet? See, a null value doesn't provide enough context. Why not model this as part of the lifecycle of an account rather than part of the meaning of a password?
What are the security implications of having a weak password that is empty?
Admin
Ive only been software engineering for 12 years, but my experience agrees with yours. Null clutters up your code with null checks (call a method, check for null). If the convention is the method will not return null, no null check is needed. NullPointerExceptions can be hard to debug, because often the source of the null comes from a different area of the application, and only makes itself known when a method is invoked on the null reference somewhere else far away... and were should the null check be done between that method and the next? Im often left guessing was a null check done before we got here? Should I put one here just in case? We could bandaid it by putting it just before or around the call that caused the null pointer.
Every new project or feature I develop I try to avoid the use of null, using empty values (Optionals, collections, Strings, etc) where possible and for true error conditions throwing an exception. These projects/features have been the most solid pieces of software Ive written.
Admin
https://imgflip.com/gif/3xp43f
Admin
Sigh. I used the null vs blank password as an example, there are other usages. But let's go with that - for some things, passwords can be optional, so you allow empty passwords in that case (if you can't see any usage for this, I can't help you). If the returned password is NULL, then it hasn't been set yet and you can display a "Do you want to set a password to access this app/service?". If it's blank (and blank is allowed), then you can access the service with no password. Not that hard, is it? There are many ways to do this, and many usages for NULL values, I was just pointing out one of them.
Admin
And there are many ways to do this without the use of null. What you're describing here here is public vs private information rather than something inherent to the existence of a password. Does there need to be role based access control if it's private (something that can't be handled with just passwords alone)? A password is used for identification verification, and access is a different context. Who has access could be public, account members, a group of people, or a single person, etc which is not correctly modeled using passwords (or lack thereof) alone. Authentication is different from authorization.
Admin
Can we have our song title back please?
Admin
Your "example" has also allowed me to highlight how I approach development. I question and probe to try to get at what the user really means/wants, because often they say one thing but mean another. If they say, "we need passwords that are nullable or empty-able", I don't just say ok and do it. I probe to figure out the why, because there is often something hiding in the details, for instance: only certain people should be able to access this resource or it could be public (authorization). After I figure out the why, I model it in software so that its clear.
//Not exactly clear about what the intent is and all too often I see code like this: public ResourceView getResource(UUID id, String password) { if (id == null) { return null; }
Resource resource = null; try { resource = resourceRepository.findBy(id); } catch (DataAccessException e) { return null; }
if (resource == null) { return null; }
if (resource.getPassword() == null || (password != null && password.equals(resource.getPassword()))) { return resource; }
return null; }
vs this:
//Somewhere, do a context mapping from a user (identity and authorization context) to an actor in the "resource" context final ResourceConsumer consumer = new ResourceConsumer(activeUser);
...
public ResourceView viewResourceWith(final ResourceId id, final ResourceConsumer consumer) { //Hey caller, don't give me nulls, aka: go fix your code Objects.requireNonNull(id); Objects.requireNonNull(consumer);
return resourceRepository.findBy(id) .filter(resource -> consumer.hasAccessToView(resource)) .map(resource -> new ResourceView(resource)) .orElseThrow(() -> new IllegalAccessException("I'm sorry " + consumer.name() + ", I'm afraid I can't do that.")); //consumer.name() if they chose not to authenticate will be 'Anonymous' }
The second is more clear about what is happening. Its better than passing/checking/returning nulls all around.
Admin
"In ALL of these cases, the caller needs to handle the response somehow: either catching the exception..."
No. The caller does not always need to catch the exception. The caller may not know what to do in the case of an error, or may be able to recover in some cases but not others. If there are errors it can recover from, it can check the type of the exception thrown and see if the error is one of those. If not, it doesn't have to make any attempt to fix it, recover from it, recognise it, react to it, or even acknowledge that it could happen (and yet it can still clean up after itself even if it has no idea what if anything went wrong), and can leave the problem to something further back up the call stack to a method that has a suitable overview of the context in which the error (as described by the exception value) can be framed.
Admin
While you're correct that throwing an exception is better than returning null, it's hardly a WTF
Admin
"No. The caller does not always need to catch the exception. "
Guys, are you purposely misreading or just want to pick a fight? You're all saying "no" and then just repeat what I basically said, hanging on a missing word or interpretation detail, using some simple example I give as if it was a real scenario/code, and plain failing to see the larger point I'm trying to make. Use your imagination!
I obviously meant that SOME caller up the chain needs to handle the exception - or you just let the application crash with "unhandled exception"? Jeeez.
My global point here is just this: Exceptions are useful, and NULLs are useful too! There's good and bad ways to use and abuse both of them! Can we agree on that?
Admin
I mean technically you're right but the actual outcome of calling Hibernate's save is that a new row gets written the database (i.e. the object got 'created') and the actual outcome of calling update is that an existing row is updated (and an exception raised if the object didn't come from the database, iirc). So having a saveEntry which creates a new record (calling session.save on the ORM, probably) and an updateEntry which updates an existing mapped object (calling session.update) is absolutely in line with how Hibernate uses those terms.
Admin
But any other mechanism requires calling code to check it too. If you throw an exception, something has to catch that (and there's the performance hit too). If you use some other special value, like empty strings or collections, something has to check that as well - but now you make it impossible to actually have 0 items or whatever and distinguish that from the case where you don't have any data. Neither of those is better than returning a null which you have to check for but which can have a clear meaning.
In the example here, saveEntry can reasonably do three things:
Or let's imagine another scenario
float? getMarketClosePrice(IdType commodity, int dayNo)
(setting aside the matter of how you represent a day)
null has a clear and obvious meaning here (there is no price for that day, because trading was suspended or it wasn't a trading day). If you're drawing a graph, you want to leave a gap on that day. That isn't an exceptional case, and there is no other value that can represent 'no data'. If you don't use null for that, how do you represent it?
Admin
List<Resource> resources = resourceRepository.findCreatedBetween(date1, date2);
for (Resource resource : resources) {...}
resources.stream().forEach(...)
Each of those styles do not require an explicit check for whether resources are empty... it either does stuff with them or if there aren't any, it does not.
An exceptional case in the above, would be let say, there was a network issue connecting to the database. There is really no way to handle that (it could be argued that it should retry, but even then most of the time, if there were network issues, its not going to resolve itself and the retry's most of the time would fail). The exception to bubble all the way up until the point where its determined, and report a message that conveys hey our infrastructure is having issues, please try again later or please report this to support. The other type of exceptions I would expect to encounter are programmer error, like say you passed in an invalid value to a method call. I would expect it to blow up, telling me what I did wrong, so I can correct it. Exceptions for user errors on the other hand, depending on the situation I may use either a conditional check or throw an exception (knowing the 'potential' performance risk). Exceptions should not be used for control flow, so generally I would defer to the conditional check.
Admin
For optionals, it works well to use the "functional" approach so that it forces you to handle both the Some and Empty cases implicitly (think map, filter, and then orElse, orElseThrow, etc).
With strings, its much easier to parse if (string.isEmtpy()) than it is if (string == null || string.isEmpty())... of its various forms. You reduce the number of string == null by guaranteeing that strings will never be null, so you can know with confidence that you can use its methods without causing a nullpointerexception or having Apache String utils just to do 'pretty' empty checks that check for null as well. There is also the potential of forgetting to put in a null check, and if not caught, can make it to a production environment.
Some strings will make it to the UI or exposed contract of an API. How are nulls handled? Some will show "null", some will convert null to display "". Its a good laugh to see null, as is evidenced by the weekly Error'd articles which often have 'null' issues. In the event you don't have a "valid" string, by guaranteeing you will always have at least an empty string, the client will always have something to print. If its not something the backend expects, you're right, you'll still have a check, but you won't need a null check as well. Eliminating null strings does not eliminate the need for validation in either direction (request or response).
Admin
For things where there isn't an 'empty' value, I would use an optional. The final value, will depend on the requirements. Here are a few impls that I have used in these scenarios: given the signature:
Optional<BigDecimal> marketClosePrice(IdType commodity, int dayNo);
We could do:
final String marketClosePriceDisplay = marketClosePrice(IdType commodity, int dayNo) .map(price -> String.valueOf(price)) .orElse("");
System.out.println(marketClosePriceDisplay);
or
// For using a value in a calculation, where null may be a problem, but the calculation can continue on with zero or when the client agrees a zero value makes sense instead of 'empty': final BigDecimal marketClosePrice = marketClosePrice(IdType commodity, int dayNo).orElse(BigDecimal.ZERO); // do some calculation, still being mindful of potential divide by zero issues.
When dealing with monetary values, null doesn't make sense and is effectively saying you've got 0 (generalizing). You either have money or you don't. When I think of not having money, I have 0 in my account, not null. Id suggest the following signature as a potential replacement candidate instead:
// Non null value returned, as a contract, be it negative, positive or 0, but largely depends on the domain. Id never expect to see null on a dashboard ticker. BigDecimal marketClosePrice(IdType commodity, int dayNo);
But lets consider an inventory system where there is an item but no price info yet. One might put price as an attribute of an item. That could be modeled by Item {Optional<BigDecimal> price();} Or Id probe an ask... if there is no price today and tomorrow there is, do we need to track the pricing history? Which could lead to another domain object that provides the history, with the most recent the current price. But this insinuates a collection, naive approach: Item { List<BigDecimal> priceHistory(); } or maybe the price is based on region, and the region has no price yet. There are many ways this could play out/be modeled without null.
Admin
Handling nulls is something we all have to be prepared to do, because they come at us and that's life.
Deliberately returning them I'm not so sure about ... I get the logic in some cases.
I'm not a fan, but then maybe it's right/unavoidable in certain circumstances. I'd say rare ones, outnumbered by the weird contorted shite I've seen some people do to "avoid" returning a null because they couldn't figure our a better route through the logic. YES:NO:FILE_NOT_FOUND
Admin
That's fair - though an optional is just a way of codifying the 'null as missing value' that I was talking about (indeed, it's called 'nullable' in .Net). A lot of code though, likely including that in the WTF, was written before optionals were a thing in mainstream languages. At least in Java, it also doubles the memory usage because Optional<T> is just a wrapper class and hence you're creating another object.
Disagree.
decimal? bankBalance { get; }
The third case may not have any obvious use case (maybe you're asking for a value in a currency with a volatile exchange rate), but it's certainly a different meaning to the second.
Admin
Fair point, I also try to avoid the "tristate boolean" situation as well. The classic situation you brought up was probably to try avoid exception/error handling, when in that case it probably was appropriate to do exception handling... don't know for sure because well... not familiar with the domain. Flags are another one of those things that seem innocuous at first... "oh just add a flag for that, super easy...". It maybe fine for a flag or 2, but when you start adding a bunch, and there is some cross over states, or conflicting states, you need to stop and ask, "should these be modeled in a different workflow or lifecycle that makes sense?"